Fix duplicate popup displays

This commit is contained in:
Manfred Karrer 2016-03-09 21:10:15 +01:00
parent 7612e53271
commit f706ddaad4
36 changed files with 547 additions and 436 deletions

View File

@ -333,7 +333,7 @@ public class TradeManager {
}
// If trade was completed (closed without fault but might be closed by a dispute) we move it to the closed trades
private void addTradeToClosedTrades(Trade trade) {
public void addTradeToClosedTrades(Trade trade) {
trades.remove(trade);
closedTradableManager.add(trade);
}

View File

@ -983,7 +983,7 @@ textfield */
-fx-background-color: linear-gradient(to bottom, #fcfcfc, #e5e5e5);
-fx-background-radius: 5 5 5 5;
-fx-background-insets: 5 5 20 20;
-fx-effect: dropshadow(gaussian, #666, 10, 0, 2, 2);
-fx-effect: dropshadow(gaussian, #333, 12, 0, -1, 3);
}
.popup-icon-information {

View File

@ -109,7 +109,7 @@ public class AddressTextField extends AnchorPane {
PopOver popOver = new PopOver(pane);
popOver.setDetachedTitle("Scan QR code for this address");
popOver.setDetached(true);
popOver.setOnHiding(windowEvent -> MainView.removeBlur());
popOver.setOnHiding(windowEvent -> MainView.removeEffect());
Window window = getScene().getWindow();
double x = Math.round(window.getX() + (window.getWidth() - 320) / 2);

View File

@ -67,15 +67,19 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
}
public static void blurLight() {
transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, true, false, 5);
transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.1, false, 5);
}
public static void blurUltraLight() {
transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, true, false, 2);
transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.1, false, 2);
}
public static void removeBlur() {
transitions.removeBlur(MainView.rootContainer);
public static void darken() {
transitions.darken(MainView.rootContainer, Transitions.DEFAULT_DURATION, false);
}
public static void removeEffect() {
transitions.removeEffect(MainView.rootContainer);
}
private final ToggleGroup navButtons = new ToggleGroup();

View File

@ -138,11 +138,11 @@ public class AltCoinAccountsView extends ActivatableViewAndModel<GridPane, AltCo
private void onDeleteAccount(PaymentAccount paymentAccount) {
new Popup().warning("Do you really want to delete the selected account?")
.actionButtonText("Yes")
.closeButtonText("Cancel")
.onAction(() -> {
model.onDeleteAccount(paymentAccount);
removeSelectAccountForm();
})
.closeButtonText("Cancel")
.show();
}

View File

@ -140,11 +140,11 @@ public class FiatAccountsView extends ActivatableViewAndModel<GridPane, FiatAcco
private void onDeleteAccount(PaymentAccount paymentAccount) {
new Popup().warning("Do you really want to delete the selected account?")
.actionButtonText("Yes")
.closeButtonText("Cancel")
.onAction(() -> {
model.onDeleteAccount(paymentAccount);
removeSelectAccountForm();
})
.closeButtonText("Cancel")
.show();
}

View File

@ -113,12 +113,12 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
if (preferences.showAgain(key)) {
new Popup().warning("You have not setup a wallet password which would protect the display of the seed words.\n\n" +
"Do you want to display the seed words?")
.closeButtonText("Yes, and don't ask me again")
.onClose(() -> {
.actionButtonText("Yes, and don't ask me again")
.onAction(() -> {
preferences.dontShowAgain(key, true);
showSeedScreen(keyChainSeed);
})
.actionButtonText("No")
.closeButtonText("No")
.show();
} else {
showSeedScreen(keyChainSeed);

View File

@ -136,11 +136,8 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
"at the Bitsquare.io web page.\n\n" +
"If you are sure you want to open a support ticket please select the trade which causes the problem " +
"under \"Portfolio/Open trades\" and type the key combination \"cmd + o\" to open the support ticket.")
.closeButtonText("Go to \"Open trades\"")
.onClose(() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class))
.actionButtonText("Close")
.onAction(() -> {
})
.actionButtonText("Go to \"Open trades\"")
.onAction(() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class))
.dontShowAgainId(key, preferences)
.show();
}

View File

@ -92,11 +92,9 @@ public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
"Withdrawing funds can be done after a trade is completed.\n\n" +
"Dedicated wallets help protect user privacy and prevent leaking information of previous trades to other" +
"traders.")
.closeButtonText("I want to learn more")
.onClose(() -> Utilities.openWebPage("https://bitsquare.io/faq"))
.actionButtonText("I understand")
.onAction(() -> {
})
.closeButtonText("I understand")
.actionButtonText("Visit FAQ web page")
.onAction(() -> Utilities.openWebPage("https://bitsquare.io/faq"))
.dontShowAgainId(key, preferences)
.show();
}

View File

@ -139,8 +139,8 @@ public class MarketsChartsView extends ActivatableViewAndModel<VBox, MarketsChar
xAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(xAxis, "", ""));
});
buyOfferTableView.setItems(model.getBuyOfferList());
sellOfferTableView.setItems(model.getSellOfferList());
buyOfferTableView.setItems(model.getTop3BuyOfferList());
sellOfferTableView.setItems(model.getTop3SellOfferList());
updateChartData();
}
@ -155,10 +155,10 @@ public class MarketsChartsView extends ActivatableViewAndModel<VBox, MarketsChar
private Tuple3<TableView<Offer>, VBox, Button> getOfferTable(Offer.Direction direction) {
TableView<Offer> tableView = new TableView<>();
tableView.setMinHeight(99);
tableView.setMaxHeight(99);
tableView.setMinHeight(100);
tableView.setMaxHeight(100);
tableView.setMinWidth(390);
tableView.setMouseTransparent(true);
// tableView.setMouseTransparent(true);
// price
TableColumn<Offer, Offer> priceColumn = new TableColumn<>();

View File

@ -50,8 +50,8 @@ class MarketsChartsViewModel extends ActivatableViewModel {
private final List<XYChart.Data> sellData = new ArrayList();
private final ObservableList<OfferBookListItem> offerBookListItems;
private final ListChangeListener<OfferBookListItem> listChangeListener;
private final ObservableList<Offer> buyOfferList = FXCollections.observableArrayList();
private final ObservableList<Offer> sellOfferList = FXCollections.observableArrayList();
private final ObservableList<Offer> top3BuyOfferList = FXCollections.observableArrayList();
private final ObservableList<Offer> top3SellOfferList = FXCollections.observableArrayList();
///////////////////////////////////////////////////////////////////////////////////////////
@ -83,13 +83,8 @@ class MarketsChartsViewModel extends ActivatableViewModel {
}
private void updateChartData(ObservableList<OfferBookListItem> offerBookListItems) {
List<Offer> offerList = offerBookListItems.stream()
List<Offer> allBuyOffers = offerBookListItems.stream()
.map(OfferBookListItem::getOffer)
.collect(Collectors.toList());
buyOfferList.clear();
buyOfferList.addAll(offerList
.stream()
.filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode())
&& e.getDirection().equals(Offer.Direction.BUY))
.sorted((o1, o2) -> {
@ -99,13 +94,12 @@ class MarketsChartsViewModel extends ActivatableViewModel {
return a < b ? 1 : -1;
return 0;
})
.collect(Collectors.toList()));
buyOfferList.subList(0, Math.min(3, buyOfferList.size()));
iterateBuyOffers(buyOfferList, Offer.Direction.BUY, buyData);
.collect(Collectors.toList());
top3BuyOfferList.setAll(allBuyOffers.subList(0, Math.min(3, allBuyOffers.size())));
buildChartDataItems(allBuyOffers, Offer.Direction.BUY, buyData);
sellOfferList.clear();
sellOfferList.addAll(offerList
.stream()
List<Offer> allSellOffers = offerBookListItems.stream()
.map(OfferBookListItem::getOffer)
.filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode())
&& e.getDirection().equals(Offer.Direction.SELL))
.sorted((o1, o2) -> {
@ -115,12 +109,12 @@ class MarketsChartsViewModel extends ActivatableViewModel {
return a > b ? 1 : -1;
return 0;
})
.collect(Collectors.toList()));
sellOfferList.subList(0, Math.min(3, sellOfferList.size()));
iterateBuyOffers(sellOfferList, Offer.Direction.SELL, sellData);
.collect(Collectors.toList());
top3SellOfferList.setAll(allSellOffers.subList(0, Math.min(3, allSellOffers.size())));
buildChartDataItems(allSellOffers, Offer.Direction.SELL, sellData);
}
private void iterateBuyOffers(List<Offer> sortedList, Offer.Direction direction, List<XYChart.Data> data) {
private void buildChartDataItems(List<Offer> sortedList, Offer.Direction direction, List<XYChart.Data> data) {
data.clear();
double accumulatedAmount = 0;
for (Offer offer : sortedList) {
@ -165,12 +159,12 @@ class MarketsChartsViewModel extends ActivatableViewModel {
return offerBookListItems;
}
public ObservableList<Offer> getBuyOfferList() {
return buyOfferList;
public ObservableList<Offer> getTop3BuyOfferList() {
return top3BuyOfferList;
}
public ObservableList<Offer> getSellOfferList() {
return sellOfferList;
public ObservableList<Offer> getTop3SellOfferList() {
return top3SellOfferList;
}
public ObservableList<TradeCurrency> getTradeCurrencies() {

View File

@ -208,7 +208,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
public void onClose() {
// 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" +
new Popup().information("You have already funds paid in.\n" +
"In the \"Funds/Available for withdrawal\" section you can withdraw those funds.").show();
}
@ -262,11 +262,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
new Popup().backgroundInfo("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader.\n" +
"It will be refunded to you after the trade has successfully completed.")
.closeButtonText("I want to learn more")
.onClose(() -> Utilities.openWebPage("https://bitsquare.io/faq#6"))
.actionButtonText("I understand")
.onAction(() -> {
})
.actionButtonText("Visit FAQ web page")
.onAction(() -> Utilities.openWebPage("https://bitsquare.io/faq#6"))
.closeButtonText("I understand")
.dontShowAgainId(key, preferences)
.show();
@ -281,7 +279,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
"(you can copy the address in the screen below after closing that popup)\n\n" +
"Make sure you use a sufficiently high mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) +
" to avoid problems that your transaction does not get confirmed in the blockchain.\n\n" +
" to avoid problems that your transaction does not get confirmed in the blockchain.\n" +
"Transactions with a lower fee will not be accepted.\n\n" +
"You can see the status of your incoming payment and all the details in the screen below.")
.dontShowAgainId(key, preferences)
.show();
@ -731,7 +730,17 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
spinnerInfoLabel = placeOfferTuple.third;
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
cancelButton2.setOnAction(e -> close());
cancelButton2.setOnAction(e -> {
if (model.dataModel.isWalletFunded.get())
new Popup().warning("You have already paid in the funds.\n" +
"Are you sure you want to cancel.")
.actionButtonText("No")
.closeButtonText("Yes, close")
.onClose(() -> close())
.show();
else
close();
});
cancelButton2.setDefaultButton(false);
cancelButton2.setVisible(false);
cancelButton2.setId("cancel-button");

View File

@ -29,7 +29,6 @@ import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.PriceFeed;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.overlays.notifications.Notification;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
import io.bitsquare.gui.util.BSFormatter;
@ -91,7 +90,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
private BalanceListener balanceListener;
private PaymentAccount paymentAccount;
private boolean isTabSelected;
private Notification walletFundedNotification;
///////////////////////////////////////////////////////////////////////////////////////////
@ -214,6 +212,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
priceFeed.setCurrencyCode(offer.getCurrencyCode());
}
///////////////////////////////////////////////////////////////////////////////////////////
// UI actions
///////////////////////////////////////////////////////////////////////////////////////////
@ -325,17 +324,8 @@ class TakeOfferDataModel extends ActivatableDataModel {
private void updateBalance(@NotNull Coin balance) {
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
if (isWalletFunded.get()) {
if (isWalletFunded.get())
walletService.removeBalanceListener(balanceListener);
if (walletFundedNotification == null) {
walletFundedNotification = new Notification()
.headLine("Trading wallet update")
.notification("Your trading wallet is sufficiently funded.\n" +
"Amount: " + formatter.formatCoinWithCode(totalToPayAsCoin.get()))
.autoClose();
walletFundedNotification.show();
}
}
}
boolean isMinAmountLessOrEqualAmount() {

View File

@ -36,6 +36,7 @@ import io.bitsquare.gui.main.account.settings.AccountSettingsView;
import io.bitsquare.gui.main.funds.FundsView;
import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
import io.bitsquare.gui.main.offer.OfferView;
import io.bitsquare.gui.main.overlays.notifications.Notification;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
import io.bitsquare.gui.main.portfolio.PortfolioView;
@ -103,6 +104,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private SimpleBooleanProperty errorPopupDisplayed;
private ChangeListener<Coin> feeFromFundingTxListener;
private boolean offerDetailsWindowDisplayed;
private Notification walletFundedNotification;
private Subscription isWalletFundedSubscription;
///////////////////////////////////////////////////////////////////////////////////////////
@ -308,6 +311,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
if (spinner != null)
spinner.setProgress(0);
if (isWalletFundedSubscription != null)
isWalletFundedSubscription.unsubscribe();
}
@ -411,11 +417,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
new Popup().backgroundInfo("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
"The deposit will stay in your local trading wallet until the offer gets accepted by another trader.\n" +
"It will be refunded to you after the trade has successfully completed.")
.closeButtonText("I want to learn more")
.onClose(() -> Utilities.openWebPage("https://bitsquare.io/faq#6"))
.actionButtonText("I understand")
.onAction(() -> {
})
.actionButtonText("Visit FAQ web page")
.onAction(() -> Utilities.openWebPage("https://bitsquare.io/faq#6"))
.closeButtonText("I understand")
.dontShowAgainId(key, preferences)
.show();
@ -429,7 +433,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
model.getAddressAsString() + "\n(you can copy the address in the screen below after closing that popup)\n\n" +
"Make sure you use a sufficiently high mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) +
" to avoid problems that your transaction does not get confirmed in the blockchain.\n\n" +
" to avoid problems that your transaction does not get confirmed in the blockchain.\n" +
"Transactions with a lower fee will not be accepted.\n\n" +
"You can see the status of your incoming payment and all the details in the screen below.")
.dontShowAgainId(key, preferences)
.show();
@ -456,6 +461,30 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
balanceTextField.setVisible(true);
setupTotalToPayInfoIconLabel();
if (model.dataModel.isWalletFunded.get()) {
if (walletFundedNotification == null) {
walletFundedNotification = new Notification()
.headLine("Trading wallet update")
.notification("Your trading wallet was already sufficiently funded from an earlier take offer attempt.\n" +
"Amount: " + formatter.formatCoinWithCode(model.dataModel.totalToPayAsCoin.get()))
.autoClose();
walletFundedNotification.show();
}
} else {
isWalletFundedSubscription = EasyBind.subscribe(model.dataModel.isWalletFunded, isFunded -> {
if (isFunded) {
if (walletFundedNotification == null) {
walletFundedNotification = new Notification()
.headLine("Trading wallet update")
.notification("Your trading wallet is sufficiently funded.\n" +
"Amount: " + formatter.formatCoinWithCode(model.dataModel.totalToPayAsCoin.get()))
.autoClose();
walletFundedNotification.show();
}
}
});
}
}
@ -632,7 +661,17 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
spinnerInfoLabel = takeOfferTuple.third;
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
cancelButton2.setOnAction(e -> close());
cancelButton2.setOnAction(e -> {
if (model.dataModel.isWalletFunded.get())
new Popup().warning("You have already paid in the funds.\n" +
"Are you sure you want to cancel.")
.actionButtonText("Yes, cancel")
.onAction(() -> close())
.closeButtonText("No")
.show();
else
close();
});
cancelButton2.setDefaultButton(false);
cancelButton2.setVisible(false);
cancelButton2.setId("cancel-button");

View File

@ -17,7 +17,6 @@
package io.bitsquare.gui.main.overlays;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
@ -34,10 +33,14 @@ import javafx.collections.ObservableList;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Camera;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
@ -55,6 +58,50 @@ import static io.bitsquare.gui.util.FormBuilder.addCheckBox;
public abstract class Overlay<T extends Overlay> {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
///////////////////////////////////////////////////////////////////////////////////////////
// Enum
///////////////////////////////////////////////////////////////////////////////////////////
private enum AnimationType {
FadeInAtCenter,
SlideDownFromCenterTop,
SlideFromRightTop,
ScaleDownToCenter,
ScaleFromCenter,
ScaleYFromCenter
}
private enum ChangeBackgroundType {
BlurLight,
BlurUltraLight,
Darken
}
protected enum Type {
Undefined(AnimationType.ScaleFromCenter, ChangeBackgroundType.BlurLight),
Notification(AnimationType.SlideFromRightTop, ChangeBackgroundType.BlurLight),
BackgroundInfo(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.BlurUltraLight),
Feedback(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.Darken),
Information(AnimationType.FadeInAtCenter, ChangeBackgroundType.BlurLight),
Instruction(AnimationType.ScaleFromCenter, ChangeBackgroundType.BlurLight),
Attention(AnimationType.ScaleFromCenter, ChangeBackgroundType.BlurLight),
Confirmation(AnimationType.ScaleYFromCenter, ChangeBackgroundType.BlurLight),
Warning(AnimationType.ScaleDownToCenter, ChangeBackgroundType.BlurLight),
Error(AnimationType.ScaleDownToCenter, ChangeBackgroundType.BlurLight);
public AnimationType animationType;
public ChangeBackgroundType changeBackgroundType;
Type(AnimationType animationType, ChangeBackgroundType changeBackgroundType) {
this.animationType = animationType;
this.changeBackgroundType = changeBackgroundType;
}
}
protected final static double DEFAULT_WIDTH = 600;
protected int rowIndex = -1;
protected String headLine;
@ -81,8 +128,7 @@ public abstract class Overlay<T extends Overlay> {
protected ChangeListener<Number> positionListener;
protected Timer centerTime;
protected double buttonDistance = 20;
private String type;
private AwesomeIcon awesomeIcon;
protected Type type = Type.Undefined;
///////////////////////////////////////////////////////////////////////////////////////////
@ -163,7 +209,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T notification(String message) {
type = "notification";
type = Type.Notification;
if (headLine == null)
this.headLine = "Notification";
this.message = message;
@ -172,7 +218,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T instruction(String message) {
type = "instruction";
type = Type.Instruction;
if (headLine == null)
this.headLine = "Instruction";
this.message = message;
@ -181,7 +227,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T backgroundInfo(String message) {
type = "backgroundInfo";
type = Type.BackgroundInfo;
if (headLine == null)
this.headLine = "Background information";
this.message = message;
@ -190,7 +236,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T feedback(String message) {
type = "feedback";
type = Type.Feedback;
if (headLine == null)
this.headLine = "Feedback";
this.message = message;
@ -199,7 +245,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T confirmation(String message) {
type = "confirmation";
type = Type.Confirmation;
if (headLine == null)
this.headLine = "Confirmation";
this.message = message;
@ -208,7 +254,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T information(String message) {
type = "information";
type = Type.Information;
if (headLine == null)
this.headLine = "Information";
this.message = message;
@ -217,8 +263,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T warning(String message) {
type = "warning";
awesomeIcon = AwesomeIcon.LIGHTBULB;
type = Type.Warning;
if (headLine == null)
this.headLine = "Warning";
@ -228,7 +273,7 @@ public abstract class Overlay<T extends Overlay> {
}
public T error(String message) {
type = "error";
type = Type.Error;
showReportErrorButtons();
if (headLine == null)
this.headLine = "Error";
@ -279,6 +324,7 @@ public abstract class Overlay<T extends Overlay> {
return (T) this;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@ -308,10 +354,18 @@ public abstract class Overlay<T extends Overlay> {
Scene rootScene = owner.getScene();
if (rootScene != null) {
stage = new Stage();
Scene scene = new Scene(gridPane);
scene.getStylesheets().setAll(rootScene.getStylesheets());
scene.setFill(Color.TRANSPARENT);
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE || e.getCode() == KeyCode.ENTER) {
e.consume();
doClose();
}
});
stage = new Stage();
stage.setScene(scene);
Window window = rootScene.getWindow();
setModality();
@ -343,183 +397,174 @@ public abstract class Overlay<T extends Overlay> {
}
}
protected void animateDisplay() {
gridPane.setOpacity(0);
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
double duration = 400;
Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
if (type.animationType == AnimationType.SlideDownFromCenterTop) {
double startY = -gridPane.getHeight();
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.translateYProperty(), startY, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.translateYProperty(), -10, interpolator)
));
} else if (type.animationType == AnimationType.ScaleFromCenter) {
double startScale = 0.25;
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), startScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), startScale, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
} else if (type.animationType == AnimationType.ScaleYFromCenter) {
double startYScale = 0.25;
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleYProperty(), startYScale, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
} else if (type.animationType == AnimationType.ScaleDownToCenter) {
double startScale = 1.1;
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), startScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), startScale, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
} else if (type.animationType == AnimationType.FadeInAtCenter) {
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator)
));
}
timeline.play();
}
protected void animateHide(Runnable onFinishedHandler) {
if ("backgroundInfo".equals(type))
animateToTop(onFinishedHandler);
else
animateToCenter(onFinishedHandler);
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
double duration = 200;
Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
if (type.animationType == AnimationType.SlideDownFromCenterTop) {
double endY = -gridPane.getHeight();
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.translateYProperty(), -10, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.translateYProperty(), endY, interpolator)
));
timeline.setOnFinished(e -> onFinishedHandler.run());
timeline.play();
} else if (type.animationType == AnimationType.ScaleFromCenter) {
double endScale = 0.25;
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), endScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), endScale, interpolator)
));
} else if (type.animationType == AnimationType.ScaleYFromCenter) {
gridPane.setRotationAxis(Rotate.X_AXIS);
Camera camera = gridPane.getScene().getCamera();
gridPane.getScene().setCamera(new PerspectiveCamera());
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.rotateProperty(), 0, interpolator),
new KeyValue(gridPane.opacityProperty(), 1, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.rotateProperty(), -90, interpolator),
new KeyValue(gridPane.opacityProperty(), 0, interpolator)
));
} else if (type.animationType == AnimationType.ScaleDownToCenter) {
double endScale = 0.1;
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), endScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), endScale, interpolator)
));
} else if (type.animationType == AnimationType.FadeInAtCenter) {
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator)
));
}
timeline.setOnFinished(e -> onFinishedHandler.run());
timeline.play();
}
protected void layout() {
if ("backgroundInfo".equals(type))
layoutAtTop();
else
layoutAtCenter();
if (owner == null)
owner = MainView.getRootContainer();
Scene rootScene = owner.getScene();
if (rootScene != null) {
Window window = rootScene.getWindow();
double titleBarHeight = window.getHeight() - rootScene.getHeight();
stage.setX(Math.round(window.getX() + (owner.getWidth() - stage.getWidth()) / 2));
if (type.animationType == AnimationType.SlideDownFromCenterTop)
stage.setY(Math.round(window.getY() + titleBarHeight));
else
stage.setY(Math.round(window.getY() + titleBarHeight + (owner.getHeight() - stage.getHeight()) / 2));
}
}
protected void addEffectToBackground() {
if ("backgroundInfo".equals(type))
if (type.changeBackgroundType == ChangeBackgroundType.BlurUltraLight)
MainView.blurUltraLight();
else
else if (type.changeBackgroundType == ChangeBackgroundType.BlurLight)
MainView.blurLight();
}
protected void animateDisplay() {
if ("backgroundInfo".equals(type))
animateFromTop();
else
animateFromCenter();
MainView.darken();
}
protected void applyStyles() {
if ("backgroundInfo".equals(type))
applyStylesTop();
if (type.animationType == AnimationType.SlideDownFromCenterTop)
gridPane.setId("popup-bg-top");
else
applyStylesCenter();
}
gridPane.setId("popup-bg");
protected void layoutAtTop() {
if (owner == null)
owner = MainView.getRootContainer();
Scene rootScene = owner.getScene();
if (rootScene != null) {
Window window = rootScene.getWindow();
double titleBarHeight = window.getHeight() - rootScene.getHeight();
stage.setX(Math.round(window.getX() + (owner.getWidth() - stage.getWidth()) / 2));
stage.setY(Math.round(window.getY() + titleBarHeight /*+ (owner.getHeight() - stage.getHeight()) / 2*/));
}
}
protected void layoutAtCenter() {
if (owner == null)
owner = MainView.getRootContainer();
Scene rootScene = owner.getScene();
if (rootScene != null) {
Window window = rootScene.getWindow();
double titleBarHeight = window.getHeight() - rootScene.getHeight();
stage.setX(Math.round(window.getX() + (owner.getWidth() - stage.getWidth()) / 2));
stage.setY(Math.round(window.getY() + titleBarHeight + (owner.getHeight() - stage.getHeight()) / 2));
}
}
protected void animateFromTop() {
gridPane.setOpacity(0);
double startY = -gridPane.getHeight();
double duration = 400;
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.translateYProperty(), startY, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.translateYProperty(), -10, interpolator)
));
timeline.play();
}
protected void animateFromCenter() {
gridPane.setOpacity(0);
double startScale = 0.25;
double duration = 400;
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), startScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), startScale, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
timeline.play();
}
protected void animateFromBottom() {
gridPane.setOpacity(0);
double startScale = 0.25;
double duration = 400;
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), startScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), startScale, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
timeline.play();
}
protected void animateToTop(Runnable onFinishedHandler) {
double endY = -gridPane.getHeight();
double duration = 200;
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.translateYProperty(), -10, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.translateYProperty(), endY, interpolator)
));
timeline.setOnFinished(e -> onFinishedHandler.run());
timeline.play();
}
protected void animateToCenter(Runnable onFinishedHandler) {
double endScale = 0.25;
double duration = 200;
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
Timeline timeline = new Timeline();
ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
keyFrames.add(new KeyFrame(Duration.millis(0),
new KeyValue(gridPane.opacityProperty(), 1, interpolator),
new KeyValue(gridPane.scaleXProperty(), 1, interpolator),
new KeyValue(gridPane.scaleYProperty(), 1, interpolator)
));
keyFrames.add(new KeyFrame(Duration.millis(duration),
new KeyValue(gridPane.opacityProperty(), 0, interpolator),
new KeyValue(gridPane.scaleXProperty(), endScale, interpolator),
new KeyValue(gridPane.scaleYProperty(), endScale, interpolator)
));
timeline.setOnFinished(e -> onFinishedHandler.run());
timeline.play();
}
protected void applyStylesTop() {
gridPane.setId("popup-bg-top");
if (headLineLabel != null)
headLineLabel.setId("popup-headline");
}
protected void applyStylesCenter() {
gridPane.setId("popup-bg");
if (headLineLabel != null)
headLineLabel.setId("popup-headline");
}
@ -529,9 +574,8 @@ public abstract class Overlay<T extends Overlay> {
stage.initModality(Modality.WINDOW_MODAL);
}
protected void removeEffectFromBackground() {
MainView.removeBlur();
MainView.removeEffect();
}
protected void addHeadLine() {
@ -641,10 +685,7 @@ public abstract class Overlay<T extends Overlay> {
protected void addCloseButton() {
closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText);
closeButton.setOnAction(event -> {
hide();
closeHandlerOptional.ifPresent(Runnable::run);
});
closeButton.setOnAction(event -> doClose());
if (actionHandlerOptional.isPresent() || actionButtonText != null) {
actionButton = new Button(actionButtonText == null ? "Ok" : actionButtonText);
@ -678,6 +719,11 @@ public abstract class Overlay<T extends Overlay> {
}
}
protected void doClose() {
hide();
closeHandlerOptional.ifPresent(Runnable::run);
}
protected void setTruncatedMessage() {
if (message != null && message.length() > 1200)
truncatedMessage = StringUtils.abbreviate(message, 1200);

View File

@ -27,12 +27,13 @@ public class Notification extends Overlay<Notification> {
public Notification() {
width = 345; // 320 visible bg because of insets
NotificationCenter.add(this);
type = Type.Notification;
}
public void onReadyForDisplay() {
super.display();
if (autoClose && autoCloseTimer == null)
autoCloseTimer = UserThread.runAfter(this::hide, 4);
autoCloseTimer = UserThread.runAfter(() -> doClose(), 5);
}
@Override

View File

@ -50,6 +50,7 @@ public class NotificationCenter {
private final TradeManager tradeManager;
private final DisputeManager disputeManager;
private Preferences preferences;
private final Navigation navigation;
private final Map<String, Subscription> disputeStateSubscriptionsMap = new HashMap<>();
@ -66,6 +67,7 @@ public class NotificationCenter {
public NotificationCenter(TradeManager tradeManager, DisputeManager disputeManager, Preferences preferences, Navigation navigation) {
this.tradeManager = tradeManager;
this.disputeManager = disputeManager;
this.preferences = preferences;
this.navigation = navigation;
EasyBind.subscribe(preferences.useAnimationsProperty(), useAnimations -> NotificationCenter.useAnimations = useAnimations);
@ -179,21 +181,29 @@ public class NotificationCenter {
}
if (message != null) {
Notification notification = new Notification().tradeHeadLine(trade.getShortId()).message(message);
if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(PendingTradesView.class)) {
notification.actionButtonText("Go to \"Open trades\"")
.onAction(() -> {
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
UserThread.runAfter(() -> {
String key = tradeState.name() + trade.getId();
if (preferences.showAgain(key)) {
Notification notification = new Notification().tradeHeadLine(trade.getShortId()).message(message);
if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(PendingTradesView.class)) {
notification.actionButtonText("Go to \"Open trades\"")
.onAction(() -> {
preferences.dontShowAgain(key, true);
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
UserThread.runAfter(() -> {
selectItemByTradeIdConsumer.accept(trade.getId());
}, 1);
})
.onClose(() -> preferences.dontShowAgain(key, true))
.show();
} else if (selectedTradeId != null && !trade.getId().equals(selectedTradeId)) {
notification.actionButtonText("Select trade")
.onAction(() -> {
preferences.dontShowAgain(key, true);
selectItemByTradeIdConsumer.accept(trade.getId());
}, 1);
})
.show();
} else if (selectedTradeId != null && !trade.getId().equals(selectedTradeId)) {
notification.actionButtonText("Select trade")
.onAction(() -> selectItemByTradeIdConsumer.accept(trade.getId()))
.show();
})
.onClose(() -> preferences.dontShowAgain(key, true))
.show();
}
}
}
}

View File

@ -56,6 +56,7 @@ public class ContractWindow extends Overlay<ContractWindow> {
@Inject
public ContractWindow(BSFormatter formatter) {
this.formatter = formatter;
type = Type.Confirmation;
}
public void show(Dispute dispute) {

View File

@ -41,6 +41,7 @@ public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow
///////////////////////////////////////////////////////////////////////////////////////////
public DisplayAlertMessageWindow() {
type = Type.Attention;
}
public void show() {

View File

@ -90,6 +90,8 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
this.disputeManager = disputeManager;
this.walletService = walletService;
this.tradeWalletService = tradeWalletService;
type = Type.Confirmation;
}
public void show(Dispute dispute) {

View File

@ -63,6 +63,8 @@ public class EmptyWalletWindow extends Overlay<EmptyWalletWindow> {
this.walletService = walletService;
this.walletPasswordWindow = walletPasswordWindow;
this.formatter = formatter;
type = Type.Instruction;
}
public void show() {

View File

@ -51,6 +51,8 @@ public class EnterPrivKeyWindow extends Overlay<EnterPrivKeyWindow> {
public EnterPrivKeyWindow() {
if (keyInputTextField != null)
keyInputTextField.textProperty().addListener(changeListener);
type = Type.Attention;
}
public void show() {

View File

@ -76,6 +76,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
this.user = user;
this.keyRing = keyRing;
this.navigation = navigation;
type = Type.Confirmation;
}
public void show(Offer offer, Coin tradeAmount) {

View File

@ -50,6 +50,7 @@ public class SelectDepositTxWindow extends Overlay<SelectDepositTxWindow> {
@Inject
public SelectDepositTxWindow() {
type = Type.Attention;
}
public void show() {

View File

@ -59,6 +59,7 @@ public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
///////////////////////////////////////////////////////////////////////////////////////////
public SendAlertMessageWindow() {
type = Type.Attention;
}
public void show() {

View File

@ -19,6 +19,7 @@ public class TacWindow extends Overlay<TacWindow> {
@Inject
public TacWindow(Preferences preferences) {
this.preferences = preferences;
type = Type.Attention;
}
public void showIfNeeded() {
@ -47,7 +48,7 @@ public class TacWindow extends Overlay<TacWindow> {
"Please be aware that using Mainnet comes with the risk to lose funds " +
"in case of software bugs.\n" +
"To limit the possible losses the maximum allowed trading amount and the " +
"security deposit have been reduced to 0.1 BTC for the alpha version " +
"security deposit have been reduced for the alpha version " +
"when using Mainnet.")
.actionButtonText("I understand and want to use Mainnet")
.closeButtonText("Restart and use Testnet")

View File

@ -63,6 +63,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
this.formatter = formatter;
this.disputeManager = disputeManager;
this.tradeManager = tradeManager;
type = Type.Confirmation;
}
public void show(Trade trade) {

View File

@ -71,6 +71,7 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
@Inject
public WalletPasswordWindow(WalletService walletService) {
this.walletService = walletService;
type = Type.Attention;
}
public void show() {

View File

@ -83,7 +83,7 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
/* if (trade.isFailedState())
return "Failed";
else*/
if (trade.getState() == Trade.State.WITHDRAW_COMPLETED) {
if (trade.getState() == Trade.State.WITHDRAW_COMPLETED || trade.getState() == Trade.State.PAYOUT_BROAD_CASTED) {
return "Completed";
} else if (trade.getDisputeState() == Trade.DisputeState.DISPUTE_CLOSED) {
return "Ticket closed";

View File

@ -102,10 +102,10 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
"When you open a support ticket the trade will be interrupted and handled by the arbitrator\n\n" +
"Unjustified support tickets (e.g. caused by usability problems or questions) will " +
"cause a loss of the security deposit by the trader who opened the ticket.")
.closeButtonText("Open support ticket")
.onClose(model.dataModel::onOpenSupportTicket)
.actionButtonText("Cancel")
.onAction(() -> popup.hide())
.actionButtonText("Open support ticket")
.onAction(model.dataModel::onOpenSupportTicket)
.closeButtonText("Cancel")
.onClose(() -> popup.hide())
.show();
}
};
@ -147,7 +147,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
root.getChildren().add(selectedSubView);
else if (root.getChildren().size() == 2)
root.getChildren().set(1, selectedSubView);
selectedSubView.activate();
}
updateTableSelection();
@ -156,6 +156,9 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
}
model.onSelectedItemChanged(selectedItem);
if (selectedSubView != null && selectedItem != null)
selectedSubView.activate();
});
selectedTableItemSubscription = EasyBind.subscribe(table.getSelectionModel().selectedItemProperty(),

View File

@ -361,11 +361,11 @@ public abstract class TradeStepView extends AnchorPane {
if (dispute.isSupportTicket()) {
setSupportOpenedHeadline();
msg = "You opened already a support ticket.\n" +
"Please communicate in the support section with the arbitrator.";
"Please communicate in the \"Support\" screen with the arbitrator.";
} else {
setDisputeOpenedHeadline();
msg = "You opened already a dispute.\n" +
"Please communicate in the support section with the arbitrator.";
"Please communicate in the \"Support\" screen with the arbitrator.";
}
if (notificationGroup != null)
notificationGroup.label.setText(msg);
@ -380,11 +380,11 @@ public abstract class TradeStepView extends AnchorPane {
if (dispute.isSupportTicket()) {
setSupportOpenedHeadline();
msg = "Your trading peer opened a support ticket due technical problems.\n" +
"Please communicate in the support section with the arbitrator.";
"Please communicate in the \"Support\" screen with the arbitrator.";
} else {
setDisputeOpenedHeadline();
msg = "Your trading peer opened a dispute.\n" +
"Please communicate in the support section with the arbitrator.";
"Please communicate in the \"Support\" screen with the arbitrator.";
}
if (notificationGroup != null)
notificationGroup.label.setText(msg);

View File

@ -22,7 +22,6 @@ import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.components.TextFieldWithCopyIcon;
import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.components.paymentmethods.*;
import io.bitsquare.gui.main.overlays.Overlay;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
@ -48,7 +47,6 @@ public class BuyerStep2View extends TradeStepView {
private Label statusLabel;
private ProgressIndicator statusProgressIndicator;
private Subscription tradeStatePropertySubscription;
private Overlay attentionRequiredPopup;
///////////////////////////////////////////////////////////////////////////////////////////
@ -67,36 +65,36 @@ public class BuyerStep2View extends TradeStepView {
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) {
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
String key = "startPaymentPopup";
if (attentionRequiredPopup == null && !BitsquareApp.DEV_MODE) {
String message = "";
if (paymentAccountContractData instanceof BlockChainAccountContractData)
message = "Your trade has reached at least one blockchain confirmation.\n\n" +
"You can wait for more confirmations if you want - 6 confirmations are considered as very secure.\n\n" +
"Please transfer from your external " +
CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()) + " wallet\n" +
model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
"Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n\n" +
"You can copy & paste the values from the main screen after closing that popup.";
else if (paymentAccountContractData != null)
message = "Your trade has reached at least one blockchain confirmation.\n\n" +
"You can wait for more confirmations if you want - 6 confirmations are considered as very secure.\n\n" +
"Please go to your online banking web page and pay " +
model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
"Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n" +
"You can copy & paste the values from the main screen after closing that popup.\n\n" +
"Please don't forget to add the reference text \"" + trade.getShortId() +
"\" so the receiver can assign your payment to this trade.\n\n" +
"DO NOT use any additional notice in the reference text like " +
"Bitcoin, Btc or Bitsquare.";
String key = "startPayment" + trade.getId();
log.error("key " + key);
String message = "";
if (paymentAccountContractData instanceof BlockChainAccountContractData)
message = "Your trade has reached at least one blockchain confirmation.\n" +
"(You can wait for more confirmations if you want - 6 confirmations are considered as very secure.)\n\n" +
"Please transfer from your external " +
CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()) + " wallet\n" +
model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
"Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n\n" +
"(You can copy & paste the values from the main screen after closing that popup.)";
else if (paymentAccountContractData != null)
message = "Your trade has reached at least one blockchain confirmation.\n" +
"(You can wait for more confirmations if you want - 6 confirmations are considered as very secure.)\n\n" +
"Please go to your online banking web page and pay " +
model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
"Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n" +
"(You can copy & paste the values from the main screen after closing that popup.)\n\n" +
"Please don't forget to add the reference text \"" + trade.getShortId() +
"\" so the receiver can assign your payment to this trade.\n\n" +
"DO NOT use any additional notice in the reference text like " +
"Bitcoin, Btc or Bitsquare.";
attentionRequiredPopup = new Popup().headLine("Attention required for trade with ID " + trade.getShortId())
if (preferences.showAgain(key)) {
new Popup().headLine("Attention required for trade with ID " + trade.getShortId())
.instruction(message)
.dontShowAgainId(key, preferences);
attentionRequiredPopup.show();
.onClose(() -> preferences.dontShowAgain(key, true))
.show();
}
} else if (state == Trade.State.BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED) {
showStatusInfo();
@ -224,15 +222,16 @@ public class BuyerStep2View extends TradeStepView {
if (model.p2PService.isBootstrapped()) {
String key = "confirmPaymentStarted";
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup()
.headLine("Confirm that you have started the payment")
Popup popup = new Popup();
popup.headLine("Confirm that you have started the payment")
.confirmation("Have you initiated the " + model.dataModel.getCurrencyCode() +
" payment to your trading partner?")
.width(700)
.dontShowAgainId(key, preferences)
.actionButtonText("Yes, I have started the payment")
.closeButtonText("No")
.onAction(this::confirmPaymentStarted)
.closeButtonText("No")
.onClose(popup::hide)
.dontShowAgainId(key, preferences)
.show();
} else {
confirmPaymentStarted();

View File

@ -19,12 +19,14 @@ package io.bitsquare.gui.main.portfolio.pendingtrades.steps.buyer;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.app.Log;
import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.Restrictions;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.funds.FundsView;
import io.bitsquare.gui.main.funds.transactions.TransactionsView;
import io.bitsquare.gui.main.overlays.notifications.Notification;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
@ -116,14 +118,17 @@ public class BuyerStep5View extends TradeStepView {
withdrawButton = addButtonAfterGroup(gridPane, ++gridRow, "Withdraw to external wallet");
withdrawButton.setOnAction(e -> reviewWithdrawal());
String key = "tradeCompleteInfo";
if (BitsquareApp.DEV_MODE)
if (BitsquareApp.DEV_MODE) {
withdrawAddressTextField.setText("mi8k5f9L972VgDaT4LgjAhriC9hHEPL7EW");
else
new Popup().headLine("Trade completed")
.instruction("You can withdraw your funds now to your external Bitcoin wallet.")
.dontShowAgainId(key, preferences)
.show();
} else {
String key = "tradeCompleted" + trade.getId();
if (preferences.showAgain(key))
new Notification().headLine("Trade completed")
.notification("You can withdraw your funds now to your external Bitcoin wallet.")
.onClose(() -> preferences.dontShowAgain(key, true))
.autoClose()
.show();
}
}
private void doWithdrawal() {
@ -131,8 +136,9 @@ public class BuyerStep5View extends TradeStepView {
model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(),
() -> {
String key = "tradeCompleteWithdrawCompletedInfo";
new Popup().headLine("Withdrawal completed").instruction("Your completed trades are stored under \"Portfolio/History\".\n" +
"You can review all your bitcoin transactions under \"Funds/Transactions\"")
new Popup().headLine("Withdrawal completed")
.feedback("Your completed trades are stored under \"Portfolio/History\".\n" +
"You can review all your bitcoin transactions under \"Funds/Transactions\"")
.actionButtonText("Go to \"Transactions\"")
.onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class))
.dontShowAgainId(key, preferences)
@ -151,45 +157,54 @@ public class BuyerStep5View extends TradeStepView {
private void reviewWithdrawal() {
Coin senderAmount = trade.getPayoutAmount();
WalletService walletService = model.dataModel.walletService;
String fromAddresses = walletService.getAddressEntryByOfferId(trade.getId()).getAddressString();
AddressEntry fromAddressesEntry = walletService.getAddressEntryByOfferId(trade.getId());
String fromAddresses = fromAddressesEntry.getAddressString();
String toAddresses = withdrawAddressTextField.getText();
if (Restrictions.isAboveFixedTxFeeAndDust(senderAmount)) {
try {
Coin requiredFee = walletService.getRequiredFee(fromAddresses, toAddresses, senderAmount, null);
Coin receiverAmount = senderAmount.subtract(requiredFee);
if (BitsquareApp.DEV_MODE) {
doWithdrawal();
} else {
BSFormatter formatter = model.formatter;
String key = "reviewWithdrawalAtTradeComplete";
if (preferences.showAgain(key)) {
new Popup().headLine("Confirm withdrawal request")
.confirmation("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" +
"From address: " + fromAddresses + "\n" +
"To receiving address: " + toAddresses + ".\n" +
"Required transaction fee is: " + formatter.formatCoinWithCode(requiredFee) + "\n\n" +
"The recipient will receive: " + formatter.formatCoinWithCode(receiverAmount) + "\n\n" +
"Are you sure you want to withdraw that amount?")
.actionButtonText("Yes")
.onAction(this::doWithdrawal)
.closeButtonText("Cancel")
.onClose(() -> withdrawButton.setDisable(false))
.dontShowAgainId(key, preferences)
.show();
} else {
doWithdrawal();
}
}
} catch (AddressFormatException e) {
e.printStackTrace();
log.error(e.getMessage());
}
// TODO at some error situation it can be tha the funds are already paid out and we get stuck here
// need handling to remove the trade (planned for next release)
Coin balance = walletService.getBalanceForAddress(fromAddressesEntry.getAddress());
if (balance.isZero()) {
new Popup().warning("Your funds have already been withdrawn.\nPlease check the transaction history.").show();
model.dataModel.tradeManager.addTradeToClosedTrades(trade);
} else {
new Popup()
.warning("The amount to transfer is lower than the transaction fee and the min. possible tx value (dust).")
.show();
if (Restrictions.isAboveFixedTxFeeAndDust(senderAmount) && !toAddresses.isEmpty()) {
try {
Coin requiredFee = walletService.getRequiredFee(fromAddresses, toAddresses, senderAmount, null);
Coin receiverAmount = senderAmount.subtract(requiredFee);
if (BitsquareApp.DEV_MODE) {
doWithdrawal();
} else {
BSFormatter formatter = model.formatter;
String key = "reviewWithdrawalAtTradeComplete";
if (preferences.showAgain(key)) {
new Popup().headLine("Confirm withdrawal request")
.confirmation("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" +
"From address: " + fromAddresses + "\n" +
"To receiving address: " + toAddresses + ".\n" +
"Required transaction fee is: " + formatter.formatCoinWithCode(requiredFee) + "\n\n" +
"The recipient will receive: " + formatter.formatCoinWithCode(receiverAmount) + "\n\n" +
"Are you sure you want to proceed with the withdrawal?")
.closeButtonText("Cancel")
.onClose(() -> withdrawButton.setDisable(false))
.actionButtonText("Yes")
.onAction(this::doWithdrawal)
.dontShowAgainId(key, preferences)
.show();
} else {
doWithdrawal();
}
}
} catch (AddressFormatException e) {
e.printStackTrace();
log.error(e.getMessage());
}
} else {
new Popup()
.warning("The amount to transfer is lower than the transaction fee and the min. possible tx value (dust).")
.show();
}
}
}

View File

@ -21,7 +21,6 @@ import io.bitsquare.app.BitsquareApp;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.components.TextFieldWithCopyIcon;
import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.main.overlays.Overlay;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
@ -49,7 +48,6 @@ public class SellerStep3View extends TradeStepView {
private Label statusLabel;
private ProgressIndicator statusProgressIndicator;
private Subscription tradeStatePropertySubscription;
private Overlay attentionRequiredPopup;
///////////////////////////////////////////////////////////////////////////////////////////
@ -67,34 +65,33 @@ public class SellerStep3View extends TradeStepView {
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) {
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
String key = "confirmPaymentPopup";
if (attentionRequiredPopup == null && !BitsquareApp.DEV_MODE) {
String message;
String tradeAmountWithCode = model.formatter.formatFiatWithCode(trade.getTradeVolume());
String currencyName = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode());
if (paymentAccountContractData instanceof BlockChainAccountContractData) {
String address = ((BlockChainAccountContractData) paymentAccountContractData).getAddress();
message = "Your trading partner has confirmed that he initiated the " + currencyName + " payment.\n\n" +
"Please check on your favorite " + currencyName +
" blockchain explorer if the transaction to your receiving address\n" +
"" + address + "\n" +
"has already sufficient blockchain confirmations.\n" +
"The payment amount has to be " + tradeAmountWithCode + "\n\n" +
"You can copy & paste your " + currencyName + " address from the main screen after " +
"closing that popup.";
} else {
message = "Your trading partner has confirmed that he initiated the " + currencyName + " payment.\n\n" +
"Please go to your online banking web page and check if you have received " +
tradeAmountWithCode + " from the bitcoin buyer.\n\n" +
"The reference text of the transaction is: \"" + trade.getShortId() + "\"";
}
attentionRequiredPopup = new Popup().headLine("Attention required for trade with ID " + trade.getShortId())
.instruction(message)
.dontShowAgainId(key, preferences);
attentionRequiredPopup.show();
String key = "confirmPayment" + trade.getId();
String message;
String tradeAmountWithCode = model.formatter.formatFiatWithCode(trade.getTradeVolume());
String currencyName = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode());
if (paymentAccountContractData instanceof BlockChainAccountContractData) {
String address = ((BlockChainAccountContractData) paymentAccountContractData).getAddress();
message = "Your trading partner has confirmed that he initiated the " + currencyName + " payment.\n\n" +
"Please check on your favorite " + currencyName +
" blockchain explorer if the transaction to your receiving address\n" +
"" + address + "\n" +
"has already sufficient blockchain confirmations.\n" +
"The payment amount has to be " + tradeAmountWithCode + "\n\n" +
"You can copy & paste your " + currencyName + " address from the main screen after " +
"closing that popup.";
} else {
message = "Your trading partner has confirmed that he initiated the " + currencyName + " payment.\n\n" +
"Please go to your online banking web page and check if you have received " +
tradeAmountWithCode + " from the bitcoin buyer.\n\n" +
"The reference text of the transaction is: \"" + trade.getShortId() + "\"";
}
if (preferences.showAgain(key)) {
new Popup().headLine("Attention required for trade with ID " + trade.getShortId())
.instruction(message)
.onClose(() -> preferences.dontShowAgain(key, true))
.show();
}
} else if (state == Trade.State.SELLER_CONFIRMED_FIAT_PAYMENT_RECEIPT) {
showStatusInfo();
statusLabel.setText("Sending confirmation...");
@ -238,10 +235,10 @@ public class SellerStep3View extends TradeStepView {
"Please note that as soon you have confirmed the receipt, the locked trade amount will be released " +
"to the bitcoin buyer and the security deposit will be refunded.")
.width(700)
.dontShowAgainId(key, preferences)
.actionButtonText("Yes, I have received the payment")
.closeButtonText("Cancel")
.onAction(this::confirmPaymentReceived)
.closeButtonText("Cancel")
.dontShowAgainId(key, preferences)
.show();
} else {
confirmPaymentReceived();

View File

@ -44,7 +44,6 @@ import javafx.util.Callback;
import javafx.util.StringConverter;
import javax.inject.Inject;
import java.util.Locale;
import static io.bitsquare.gui.util.FormBuilder.*;
@ -54,7 +53,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
// not supported yet
//private ComboBox<String> btcDenominationComboBox;
private ComboBox<BlockChainExplorer> blockChainExplorerComboBox;
private ComboBox<String> userLanguageComboBox;
// private ComboBox<String> userLanguageComboBox;
private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox;
private CheckBox useAnimationsCheckBox, autoSelectArbitratorsCheckBox;
@ -275,11 +274,11 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
}
private void initializeOtherOptions() {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 3, "General preferences", Layout.GROUP_DISTANCE);
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 2, "General preferences", Layout.GROUP_DISTANCE);
GridPane.setColumnSpan(titledGroupBg, 4);
userLanguageComboBox = addLabelComboBox(root, gridRow, "Language:", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
// userLanguageComboBox = addLabelComboBox(root, gridRow, "Language:", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
// btcDenominationComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin denomination:").second;
blockChainExplorerComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin block explorer:").second;
blockChainExplorerComboBox = addLabelComboBox(root, gridRow, "Bitcoin block explorer:", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
autoSelectArbitratorsCheckBox = addLabelCheckBox(root, ++gridRow, "Auto select arbitrators:", "").second;
// TODO need a bit extra work to separate trade and non trade tx fees before it can be used
@ -352,7 +351,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
btcDenominationComboBox.getSelectionModel().select(getBtcDenomination());
btcDenominationComboBox.setOnAction(e -> onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/
userLanguageComboBox.setItems(languageCodes);
/* userLanguageComboBox.setItems(languageCodes);
userLanguageComboBox.getSelectionModel().select(preferences.getPreferredLocale().getLanguage());
userLanguageComboBox.setConverter(new StringConverter<String>() {
@Override
@ -368,7 +367,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
userLanguageComboBox.setOnAction(e -> {
String code = userLanguageComboBox.getSelectionModel().getSelectedItem();
preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry()));
});
});*/
blockChainExplorerComboBox.setItems(blockExplorers);
@ -410,7 +409,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
private void deactivateOtherOptions() {
//btcDenominationComboBox.setOnAction(null);
userLanguageComboBox.setOnAction(null);
// userLanguageComboBox.setOnAction(null);
blockChainExplorerComboBox.setOnAction(null);
// transactionFeeInputTextField.textProperty().unbind();
/// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);

View File

@ -35,7 +35,7 @@ public class Transitions {
public final static int DEFAULT_DURATION = 600;
private final Preferences preferences;
private Timeline removeBlurTimeLine;
private Timeline removeEffectTimeLine;
@Inject
public Transitions(Preferences preferences) {
@ -91,65 +91,61 @@ public class Transitions {
// Blur
public void blur(Node node) {
blur(node, DEFAULT_DURATION, true, false, 15);
blur(node, DEFAULT_DURATION, -0.1, false, 15);
}
public void blur(Node node, int duration, boolean useDarken, boolean removeNode, double blurRadius) {
if (removeBlurTimeLine != null)
removeBlurTimeLine.stop();
public void blur(Node node, int duration, double brightness, boolean removeNode, double blurRadius) {
if (removeEffectTimeLine != null)
removeEffectTimeLine.stop();
node.setMouseTransparent(true);
GaussianBlur blur = new GaussianBlur(0.0);
Timeline timeline = new Timeline();
KeyValue kv1 = new KeyValue(blur.radiusProperty(), blurRadius);
KeyFrame kf1 = new KeyFrame(Duration.millis(getDuration(duration)), kv1);
if (useDarken) {
ColorAdjust darken = new ColorAdjust();
darken.setBrightness(0.0);
blur.setInput(darken);
KeyValue kv2 = new KeyValue(darken.brightnessProperty(), -0.1);
KeyFrame kf2 = new KeyFrame(Duration.millis(getDuration(duration)), kv2);
timeline.getKeyFrames().addAll(kf1, kf2);
} else {
timeline.getKeyFrames().addAll(kf1);
}
ColorAdjust darken = new ColorAdjust();
darken.setBrightness(0.0);
blur.setInput(darken);
KeyValue kv2 = new KeyValue(darken.brightnessProperty(), brightness);
KeyFrame kf2 = new KeyFrame(Duration.millis(getDuration(duration)), kv2);
timeline.getKeyFrames().addAll(kf1, kf2);
node.setEffect(blur);
if (removeNode) timeline.setOnFinished(actionEvent -> UserThread.execute(() -> ((Pane) (node.getParent()))
.getChildren().remove(node)));
timeline.play();
}
public void removeBlur(Node node) {
removeBlur(node, DEFAULT_DURATION, false);
// Darken
public void darken(Node node, int duration, boolean removeNode) {
blur(node, duration, -0.2, removeNode, 0);
}
private void removeBlur(Node node, int duration, boolean useDarken) {
public void removeEffect(Node node) {
removeEffect(node, DEFAULT_DURATION);
}
private void removeEffect(Node node, int duration) {
if (node != null) {
node.setMouseTransparent(false);
removeEffectTimeLine = new Timeline();
GaussianBlur blur = (GaussianBlur) node.getEffect();
if (blur != null) {
removeBlurTimeLine = new Timeline();
KeyValue kv1 = new KeyValue(blur.radiusProperty(), 0.0);
KeyFrame kf1 = new KeyFrame(Duration.millis(getDuration(duration)), kv1);
removeEffectTimeLine.getKeyFrames().add(kf1);
if (useDarken) {
ColorAdjust darken = (ColorAdjust) blur.getInput();
KeyValue kv2 = new KeyValue(darken.brightnessProperty(), 0.0);
KeyFrame kf2 = new KeyFrame(Duration.millis(getDuration(duration)), kv2);
removeBlurTimeLine.getKeyFrames().addAll(kf1, kf2);
} else {
removeBlurTimeLine.getKeyFrames().addAll(kf1);
}
removeBlurTimeLine.setOnFinished(actionEvent -> {
ColorAdjust darken = (ColorAdjust) blur.getInput();
KeyValue kv2 = new KeyValue(darken.brightnessProperty(), 0.0);
KeyFrame kf2 = new KeyFrame(Duration.millis(getDuration(duration)), kv2);
removeEffectTimeLine.getKeyFrames().add(kf2);
removeEffectTimeLine.setOnFinished(actionEvent -> {
node.setEffect(null);
removeBlurTimeLine = null;
removeEffectTimeLine = null;
});
removeBlurTimeLine.play();
removeEffectTimeLine.play();
} else {
node.setEffect(null);
removeEffectTimeLine = null;
}
}
}