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 // 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); trades.remove(trade);
closedTradableManager.add(trade); closedTradableManager.add(trade);
} }

View file

@ -983,7 +983,7 @@ textfield */
-fx-background-color: linear-gradient(to bottom, #fcfcfc, #e5e5e5); -fx-background-color: linear-gradient(to bottom, #fcfcfc, #e5e5e5);
-fx-background-radius: 5 5 5 5; -fx-background-radius: 5 5 5 5;
-fx-background-insets: 5 5 20 20; -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 { .popup-icon-information {

View file

@ -109,7 +109,7 @@ public class AddressTextField extends AnchorPane {
PopOver popOver = new PopOver(pane); PopOver popOver = new PopOver(pane);
popOver.setDetachedTitle("Scan QR code for this address"); popOver.setDetachedTitle("Scan QR code for this address");
popOver.setDetached(true); popOver.setDetached(true);
popOver.setOnHiding(windowEvent -> MainView.removeBlur()); popOver.setOnHiding(windowEvent -> MainView.removeEffect());
Window window = getScene().getWindow(); Window window = getScene().getWindow();
double x = Math.round(window.getX() + (window.getWidth() - 320) / 2); 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() { 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() { 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() { public static void darken() {
transitions.removeBlur(MainView.rootContainer); transitions.darken(MainView.rootContainer, Transitions.DEFAULT_DURATION, false);
}
public static void removeEffect() {
transitions.removeEffect(MainView.rootContainer);
} }
private final ToggleGroup navButtons = new ToggleGroup(); private final ToggleGroup navButtons = new ToggleGroup();

View file

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

View file

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

View file

@ -113,12 +113,12 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
if (preferences.showAgain(key)) { 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" + 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?") "Do you want to display the seed words?")
.closeButtonText("Yes, and don't ask me again") .actionButtonText("Yes, and don't ask me again")
.onClose(() -> { .onAction(() -> {
preferences.dontShowAgain(key, true); preferences.dontShowAgain(key, true);
showSeedScreen(keyChainSeed); showSeedScreen(keyChainSeed);
}) })
.actionButtonText("No") .closeButtonText("No")
.show(); .show();
} else { } else {
showSeedScreen(keyChainSeed); showSeedScreen(keyChainSeed);

View file

@ -136,11 +136,8 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
"at the Bitsquare.io web page.\n\n" + "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 " + "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.") "under \"Portfolio/Open trades\" and type the key combination \"cmd + o\" to open the support ticket.")
.closeButtonText("Go to \"Open trades\"") .actionButtonText("Go to \"Open trades\"")
.onClose(() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)) .onAction(() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class))
.actionButtonText("Close")
.onAction(() -> {
})
.dontShowAgainId(key, preferences) .dontShowAgainId(key, preferences)
.show(); .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" + "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" + "Dedicated wallets help protect user privacy and prevent leaking information of previous trades to other" +
"traders.") "traders.")
.closeButtonText("I want to learn more") .closeButtonText("I understand")
.onClose(() -> Utilities.openWebPage("https://bitsquare.io/faq")) .actionButtonText("Visit FAQ web page")
.actionButtonText("I understand") .onAction(() -> Utilities.openWebPage("https://bitsquare.io/faq"))
.onAction(() -> {
})
.dontShowAgainId(key, preferences) .dontShowAgainId(key, preferences)
.show(); .show();
} }

View file

@ -139,8 +139,8 @@ public class MarketsChartsView extends ActivatableViewAndModel<VBox, MarketsChar
xAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(xAxis, "", "")); xAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(xAxis, "", ""));
}); });
buyOfferTableView.setItems(model.getBuyOfferList()); buyOfferTableView.setItems(model.getTop3BuyOfferList());
sellOfferTableView.setItems(model.getSellOfferList()); sellOfferTableView.setItems(model.getTop3SellOfferList());
updateChartData(); updateChartData();
} }
@ -155,10 +155,10 @@ public class MarketsChartsView extends ActivatableViewAndModel<VBox, MarketsChar
private Tuple3<TableView<Offer>, VBox, Button> getOfferTable(Offer.Direction direction) { private Tuple3<TableView<Offer>, VBox, Button> getOfferTable(Offer.Direction direction) {
TableView<Offer> tableView = new TableView<>(); TableView<Offer> tableView = new TableView<>();
tableView.setMinHeight(99); tableView.setMinHeight(100);
tableView.setMaxHeight(99); tableView.setMaxHeight(100);
tableView.setMinWidth(390); tableView.setMinWidth(390);
tableView.setMouseTransparent(true); // tableView.setMouseTransparent(true);
// price // price
TableColumn<Offer, Offer> priceColumn = new TableColumn<>(); 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 List<XYChart.Data> sellData = new ArrayList();
private final ObservableList<OfferBookListItem> offerBookListItems; private final ObservableList<OfferBookListItem> offerBookListItems;
private final ListChangeListener<OfferBookListItem> listChangeListener; private final ListChangeListener<OfferBookListItem> listChangeListener;
private final ObservableList<Offer> buyOfferList = FXCollections.observableArrayList(); private final ObservableList<Offer> top3BuyOfferList = FXCollections.observableArrayList();
private final ObservableList<Offer> sellOfferList = FXCollections.observableArrayList(); private final ObservableList<Offer> top3SellOfferList = FXCollections.observableArrayList();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -83,13 +83,8 @@ class MarketsChartsViewModel extends ActivatableViewModel {
} }
private void updateChartData(ObservableList<OfferBookListItem> offerBookListItems) { private void updateChartData(ObservableList<OfferBookListItem> offerBookListItems) {
List<Offer> offerList = offerBookListItems.stream() List<Offer> allBuyOffers = offerBookListItems.stream()
.map(OfferBookListItem::getOffer) .map(OfferBookListItem::getOffer)
.collect(Collectors.toList());
buyOfferList.clear();
buyOfferList.addAll(offerList
.stream()
.filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode()) .filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode())
&& e.getDirection().equals(Offer.Direction.BUY)) && e.getDirection().equals(Offer.Direction.BUY))
.sorted((o1, o2) -> { .sorted((o1, o2) -> {
@ -99,13 +94,12 @@ class MarketsChartsViewModel extends ActivatableViewModel {
return a < b ? 1 : -1; return a < b ? 1 : -1;
return 0; return 0;
}) })
.collect(Collectors.toList())); .collect(Collectors.toList());
buyOfferList.subList(0, Math.min(3, buyOfferList.size())); top3BuyOfferList.setAll(allBuyOffers.subList(0, Math.min(3, allBuyOffers.size())));
iterateBuyOffers(buyOfferList, Offer.Direction.BUY, buyData); buildChartDataItems(allBuyOffers, Offer.Direction.BUY, buyData);
sellOfferList.clear(); List<Offer> allSellOffers = offerBookListItems.stream()
sellOfferList.addAll(offerList .map(OfferBookListItem::getOffer)
.stream()
.filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode()) .filter(e -> e.getCurrencyCode().equals(tradeCurrency.get().getCode())
&& e.getDirection().equals(Offer.Direction.SELL)) && e.getDirection().equals(Offer.Direction.SELL))
.sorted((o1, o2) -> { .sorted((o1, o2) -> {
@ -115,12 +109,12 @@ class MarketsChartsViewModel extends ActivatableViewModel {
return a > b ? 1 : -1; return a > b ? 1 : -1;
return 0; return 0;
}) })
.collect(Collectors.toList())); .collect(Collectors.toList());
sellOfferList.subList(0, Math.min(3, sellOfferList.size())); top3SellOfferList.setAll(allSellOffers.subList(0, Math.min(3, allSellOffers.size())));
iterateBuyOffers(sellOfferList, Offer.Direction.SELL, sellData); 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(); data.clear();
double accumulatedAmount = 0; double accumulatedAmount = 0;
for (Offer offer : sortedList) { for (Offer offer : sortedList) {
@ -165,12 +159,12 @@ class MarketsChartsViewModel extends ActivatableViewModel {
return offerBookListItems; return offerBookListItems;
} }
public ObservableList<Offer> getBuyOfferList() { public ObservableList<Offer> getTop3BuyOfferList() {
return buyOfferList; return top3BuyOfferList;
} }
public ObservableList<Offer> getSellOfferList() { public ObservableList<Offer> getTop3SellOfferList() {
return sellOfferList; return top3SellOfferList;
} }
public ObservableList<TradeCurrency> getTradeCurrencies() { public ObservableList<TradeCurrency> getTradeCurrencies() {

View file

@ -208,7 +208,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
public void onClose() { public void onClose() {
// we use model.placeOfferCompleted to not react on close which was triggered by a successful placeOffer // we use model.placeOfferCompleted to not react on close which was triggered by a successful placeOffer
if (model.dataModel.isWalletFunded.get() && !model.placeOfferCompleted.get()) 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(); "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" + 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" + "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.") "It will be refunded to you after the trade has successfully completed.")
.closeButtonText("I want to learn more") .actionButtonText("Visit FAQ web page")
.onClose(() -> Utilities.openWebPage("https://bitsquare.io/faq#6")) .onAction(() -> Utilities.openWebPage("https://bitsquare.io/faq#6"))
.actionButtonText("I understand") .closeButtonText("I understand")
.onAction(() -> {
})
.dontShowAgainId(key, preferences) .dontShowAgainId(key, preferences)
.show(); .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" + "(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 " + "Make sure you use a sufficiently high mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + 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.") "You can see the status of your incoming payment and all the details in the screen below.")
.dontShowAgainId(key, preferences) .dontShowAgainId(key, preferences)
.show(); .show();
@ -731,7 +730,17 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
spinnerInfoLabel = placeOfferTuple.third; spinnerInfoLabel = placeOfferTuple.third;
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel")); 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.setDefaultButton(false);
cancelButton2.setVisible(false); cancelButton2.setVisible(false);
cancelButton2.setId("cancel-button"); 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.btc.pricefeed.PriceFeed;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.gui.common.model.ActivatableDataModel; 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.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow; import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
@ -91,7 +90,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
private BalanceListener balanceListener; private BalanceListener balanceListener;
private PaymentAccount paymentAccount; private PaymentAccount paymentAccount;
private boolean isTabSelected; private boolean isTabSelected;
private Notification walletFundedNotification;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -214,6 +212,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
priceFeed.setCurrencyCode(offer.getCurrencyCode()); priceFeed.setCurrencyCode(offer.getCurrencyCode());
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// UI actions // UI actions
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -325,17 +324,8 @@ class TakeOfferDataModel extends ActivatableDataModel {
private void updateBalance(@NotNull Coin balance) { private void updateBalance(@NotNull Coin balance) {
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0); isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
if (isWalletFunded.get()) { if (isWalletFunded.get())
walletService.removeBalanceListener(balanceListener); 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() { 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.FundsView;
import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView; import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
import io.bitsquare.gui.main.offer.OfferView; 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.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow; import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
import io.bitsquare.gui.main.portfolio.PortfolioView; import io.bitsquare.gui.main.portfolio.PortfolioView;
@ -103,6 +104,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private SimpleBooleanProperty errorPopupDisplayed; private SimpleBooleanProperty errorPopupDisplayed;
private ChangeListener<Coin> feeFromFundingTxListener; private ChangeListener<Coin> feeFromFundingTxListener;
private boolean offerDetailsWindowDisplayed; private boolean offerDetailsWindowDisplayed;
private Notification walletFundedNotification;
private Subscription isWalletFundedSubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -308,6 +311,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
if (spinner != null) if (spinner != null)
spinner.setProgress(0); 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" + 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" + "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.") "It will be refunded to you after the trade has successfully completed.")
.closeButtonText("I want to learn more") .actionButtonText("Visit FAQ web page")
.onClose(() -> Utilities.openWebPage("https://bitsquare.io/faq#6")) .onAction(() -> Utilities.openWebPage("https://bitsquare.io/faq#6"))
.actionButtonText("I understand") .closeButtonText("I understand")
.onAction(() -> {
})
.dontShowAgainId(key, preferences) .dontShowAgainId(key, preferences)
.show(); .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" + 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 " + "Make sure you use a sufficiently high mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + 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.") "You can see the status of your incoming payment and all the details in the screen below.")
.dontShowAgainId(key, preferences) .dontShowAgainId(key, preferences)
.show(); .show();
@ -456,6 +461,30 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
balanceTextField.setVisible(true); balanceTextField.setVisible(true);
setupTotalToPayInfoIconLabel(); 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; spinnerInfoLabel = takeOfferTuple.third;
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel")); 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.setDefaultButton(false);
cancelButton2.setVisible(false); cancelButton2.setVisible(false);
cancelButton2.setId("cancel-button"); cancelButton2.setId("cancel-button");

View file

@ -17,7 +17,6 @@
package io.bitsquare.gui.main.overlays; package io.bitsquare.gui.main.overlays;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.common.Timer; import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
@ -34,10 +33,14 @@ import javafx.collections.ObservableList;
import javafx.geometry.HPos; import javafx.geometry.HPos;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Orientation; import javafx.geometry.Orientation;
import javafx.scene.Camera;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate;
import javafx.stage.Modality; import javafx.stage.Modality;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.StageStyle; import javafx.stage.StageStyle;
@ -55,6 +58,50 @@ import static io.bitsquare.gui.util.FormBuilder.addCheckBox;
public abstract class Overlay<T extends Overlay> { public abstract class Overlay<T extends Overlay> {
protected final Logger log = LoggerFactory.getLogger(this.getClass()); 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 final static double DEFAULT_WIDTH = 600;
protected int rowIndex = -1; protected int rowIndex = -1;
protected String headLine; protected String headLine;
@ -81,8 +128,7 @@ public abstract class Overlay<T extends Overlay> {
protected ChangeListener<Number> positionListener; protected ChangeListener<Number> positionListener;
protected Timer centerTime; protected Timer centerTime;
protected double buttonDistance = 20; protected double buttonDistance = 20;
private String type; protected Type type = Type.Undefined;
private AwesomeIcon awesomeIcon;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -163,7 +209,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T notification(String message) { public T notification(String message) {
type = "notification"; type = Type.Notification;
if (headLine == null) if (headLine == null)
this.headLine = "Notification"; this.headLine = "Notification";
this.message = message; this.message = message;
@ -172,7 +218,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T instruction(String message) { public T instruction(String message) {
type = "instruction"; type = Type.Instruction;
if (headLine == null) if (headLine == null)
this.headLine = "Instruction"; this.headLine = "Instruction";
this.message = message; this.message = message;
@ -181,7 +227,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T backgroundInfo(String message) { public T backgroundInfo(String message) {
type = "backgroundInfo"; type = Type.BackgroundInfo;
if (headLine == null) if (headLine == null)
this.headLine = "Background information"; this.headLine = "Background information";
this.message = message; this.message = message;
@ -190,7 +236,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T feedback(String message) { public T feedback(String message) {
type = "feedback"; type = Type.Feedback;
if (headLine == null) if (headLine == null)
this.headLine = "Feedback"; this.headLine = "Feedback";
this.message = message; this.message = message;
@ -199,7 +245,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T confirmation(String message) { public T confirmation(String message) {
type = "confirmation"; type = Type.Confirmation;
if (headLine == null) if (headLine == null)
this.headLine = "Confirmation"; this.headLine = "Confirmation";
this.message = message; this.message = message;
@ -208,7 +254,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T information(String message) { public T information(String message) {
type = "information"; type = Type.Information;
if (headLine == null) if (headLine == null)
this.headLine = "Information"; this.headLine = "Information";
this.message = message; this.message = message;
@ -217,8 +263,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T warning(String message) { public T warning(String message) {
type = "warning"; type = Type.Warning;
awesomeIcon = AwesomeIcon.LIGHTBULB;
if (headLine == null) if (headLine == null)
this.headLine = "Warning"; this.headLine = "Warning";
@ -228,7 +273,7 @@ public abstract class Overlay<T extends Overlay> {
} }
public T error(String message) { public T error(String message) {
type = "error"; type = Type.Error;
showReportErrorButtons(); showReportErrorButtons();
if (headLine == null) if (headLine == null)
this.headLine = "Error"; this.headLine = "Error";
@ -279,6 +324,7 @@ public abstract class Overlay<T extends Overlay> {
return (T) this; return (T) this;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Protected // Protected
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -308,10 +354,18 @@ public abstract class Overlay<T extends Overlay> {
Scene rootScene = owner.getScene(); Scene rootScene = owner.getScene();
if (rootScene != null) { if (rootScene != null) {
stage = new Stage();
Scene scene = new Scene(gridPane); Scene scene = new Scene(gridPane);
scene.getStylesheets().setAll(rootScene.getStylesheets()); scene.getStylesheets().setAll(rootScene.getStylesheets());
scene.setFill(Color.TRANSPARENT); 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); stage.setScene(scene);
Window window = rootScene.getWindow(); Window window = rootScene.getWindow();
setModality(); 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) { protected void animateHide(Runnable onFinishedHandler) {
if ("backgroundInfo".equals(type)) Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);
animateToTop(onFinishedHandler); double duration = 200;
else Timeline timeline = new Timeline();
animateToCenter(onFinishedHandler); 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() { protected void layout() {
if ("backgroundInfo".equals(type)) if (owner == null)
layoutAtTop(); owner = MainView.getRootContainer();
else Scene rootScene = owner.getScene();
layoutAtCenter(); 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() { protected void addEffectToBackground() {
if ("backgroundInfo".equals(type)) if (type.changeBackgroundType == ChangeBackgroundType.BlurUltraLight)
MainView.blurUltraLight(); MainView.blurUltraLight();
else else if (type.changeBackgroundType == ChangeBackgroundType.BlurLight)
MainView.blurLight(); MainView.blurLight();
}
protected void animateDisplay() {
if ("backgroundInfo".equals(type))
animateFromTop();
else else
animateFromCenter(); MainView.darken();
} }
protected void applyStyles() { protected void applyStyles() {
if ("backgroundInfo".equals(type)) if (type.animationType == AnimationType.SlideDownFromCenterTop)
applyStylesTop(); gridPane.setId("popup-bg-top");
else 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) if (headLineLabel != null)
headLineLabel.setId("popup-headline"); headLineLabel.setId("popup-headline");
} }
@ -529,9 +574,8 @@ public abstract class Overlay<T extends Overlay> {
stage.initModality(Modality.WINDOW_MODAL); stage.initModality(Modality.WINDOW_MODAL);
} }
protected void removeEffectFromBackground() { protected void removeEffectFromBackground() {
MainView.removeBlur(); MainView.removeEffect();
} }
protected void addHeadLine() { protected void addHeadLine() {
@ -641,10 +685,7 @@ public abstract class Overlay<T extends Overlay> {
protected void addCloseButton() { protected void addCloseButton() {
closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText); closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText);
closeButton.setOnAction(event -> { closeButton.setOnAction(event -> doClose());
hide();
closeHandlerOptional.ifPresent(Runnable::run);
});
if (actionHandlerOptional.isPresent() || actionButtonText != null) { if (actionHandlerOptional.isPresent() || actionButtonText != null) {
actionButton = new Button(actionButtonText == null ? "Ok" : actionButtonText); 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() { protected void setTruncatedMessage() {
if (message != null && message.length() > 1200) if (message != null && message.length() > 1200)
truncatedMessage = StringUtils.abbreviate(message, 1200); truncatedMessage = StringUtils.abbreviate(message, 1200);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -83,7 +83,7 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
/* if (trade.isFailedState()) /* if (trade.isFailedState())
return "Failed"; return "Failed";
else*/ else*/
if (trade.getState() == Trade.State.WITHDRAW_COMPLETED) { if (trade.getState() == Trade.State.WITHDRAW_COMPLETED || trade.getState() == Trade.State.PAYOUT_BROAD_CASTED) {
return "Completed"; return "Completed";
} else if (trade.getDisputeState() == Trade.DisputeState.DISPUTE_CLOSED) { } else if (trade.getDisputeState() == Trade.DisputeState.DISPUTE_CLOSED) {
return "Ticket 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" + "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 " + "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.") "cause a loss of the security deposit by the trader who opened the ticket.")
.closeButtonText("Open support ticket") .actionButtonText("Open support ticket")
.onClose(model.dataModel::onOpenSupportTicket) .onAction(model.dataModel::onOpenSupportTicket)
.actionButtonText("Cancel") .closeButtonText("Cancel")
.onAction(() -> popup.hide()) .onClose(() -> popup.hide())
.show(); .show();
} }
}; };
@ -147,7 +147,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
root.getChildren().add(selectedSubView); root.getChildren().add(selectedSubView);
else if (root.getChildren().size() == 2) else if (root.getChildren().size() == 2)
root.getChildren().set(1, selectedSubView); root.getChildren().set(1, selectedSubView);
selectedSubView.activate();
} }
updateTableSelection(); updateTableSelection();
@ -156,6 +156,9 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
} }
model.onSelectedItemChanged(selectedItem); model.onSelectedItemChanged(selectedItem);
if (selectedSubView != null && selectedItem != null)
selectedSubView.activate();
}); });
selectedTableItemSubscription = EasyBind.subscribe(table.getSelectionModel().selectedItemProperty(), selectedTableItemSubscription = EasyBind.subscribe(table.getSelectionModel().selectedItemProperty(),

View file

@ -361,11 +361,11 @@ public abstract class TradeStepView extends AnchorPane {
if (dispute.isSupportTicket()) { if (dispute.isSupportTicket()) {
setSupportOpenedHeadline(); setSupportOpenedHeadline();
msg = "You opened already a support ticket.\n" + 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 { } else {
setDisputeOpenedHeadline(); setDisputeOpenedHeadline();
msg = "You opened already a dispute.\n" + 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) if (notificationGroup != null)
notificationGroup.label.setText(msg); notificationGroup.label.setText(msg);
@ -380,11 +380,11 @@ public abstract class TradeStepView extends AnchorPane {
if (dispute.isSupportTicket()) { if (dispute.isSupportTicket()) {
setSupportOpenedHeadline(); setSupportOpenedHeadline();
msg = "Your trading peer opened a support ticket due technical problems.\n" + 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 { } else {
setDisputeOpenedHeadline(); setDisputeOpenedHeadline();
msg = "Your trading peer opened a dispute.\n" + 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) if (notificationGroup != null)
notificationGroup.label.setText(msg); 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.TextFieldWithCopyIcon;
import io.bitsquare.gui.components.TitledGroupBg; import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.components.paymentmethods.*; 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.overlays.popups.Popup;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
@ -48,7 +47,6 @@ public class BuyerStep2View extends TradeStepView {
private Label statusLabel; private Label statusLabel;
private ProgressIndicator statusProgressIndicator; private ProgressIndicator statusProgressIndicator;
private Subscription tradeStatePropertySubscription; private Subscription tradeStatePropertySubscription;
private Overlay attentionRequiredPopup;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -67,36 +65,36 @@ public class BuyerStep2View extends TradeStepView {
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> { tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) { if (state == Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN) {
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData(); PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
String key = "startPaymentPopup"; String key = "startPayment" + trade.getId();
if (attentionRequiredPopup == null && !BitsquareApp.DEV_MODE) { log.error("key " + key);
String message = ""; String message = "";
if (paymentAccountContractData instanceof BlockChainAccountContractData) if (paymentAccountContractData instanceof BlockChainAccountContractData)
message = "Your trade has reached at least one blockchain confirmation.\n\n" + 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" + "(You can wait for more confirmations if you want - 6 confirmations are considered as very secure.)\n\n" +
"Please transfer from your external " + "Please transfer from your external " +
CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()) + " wallet\n" + CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()) + " wallet\n" +
model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" + model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
"Here are the payment account details of the bitcoin seller:\n" + "Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n\n" + "" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n\n" +
"You can copy & paste the values from the main screen after closing that popup."; "(You can copy & paste the values from the main screen after closing that popup.)";
else if (paymentAccountContractData != null) else if (paymentAccountContractData != null)
message = "Your trade has reached at least one blockchain confirmation.\n\n" + 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" + "(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 " + "Please go to your online banking web page and pay " +
model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" + model.formatter.formatFiatWithCode(trade.getTradeVolume()) + " to the bitcoin seller.\n\n" +
"Here are the payment account details of the bitcoin seller:\n" + "Here are the payment account details of the bitcoin seller:\n" +
"" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n" + "" + paymentAccountContractData.getPaymentDetailsForTradePopup() + ".\n" +
"You can copy & paste the values from the main screen after closing that popup.\n\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() + "Please don't forget to add the reference text \"" + trade.getShortId() +
"\" so the receiver can assign your payment to this trade.\n\n" + "\" so the receiver can assign your payment to this trade.\n\n" +
"DO NOT use any additional notice in the reference text like " + "DO NOT use any additional notice in the reference text like " +
"Bitcoin, Btc or Bitsquare."; "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) .instruction(message)
.dontShowAgainId(key, preferences); .onClose(() -> preferences.dontShowAgain(key, true))
.show();
attentionRequiredPopup.show();
} }
} else if (state == Trade.State.BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED) { } else if (state == Trade.State.BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED) {
showStatusInfo(); showStatusInfo();
@ -224,15 +222,16 @@ public class BuyerStep2View extends TradeStepView {
if (model.p2PService.isBootstrapped()) { if (model.p2PService.isBootstrapped()) {
String key = "confirmPaymentStarted"; String key = "confirmPaymentStarted";
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) { if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup() Popup popup = new Popup();
.headLine("Confirm that you have started the payment") popup.headLine("Confirm that you have started the payment")
.confirmation("Have you initiated the " + model.dataModel.getCurrencyCode() + .confirmation("Have you initiated the " + model.dataModel.getCurrencyCode() +
" payment to your trading partner?") " payment to your trading partner?")
.width(700) .width(700)
.dontShowAgainId(key, preferences)
.actionButtonText("Yes, I have started the payment") .actionButtonText("Yes, I have started the payment")
.closeButtonText("No")
.onAction(this::confirmPaymentStarted) .onAction(this::confirmPaymentStarted)
.closeButtonText("No")
.onClose(popup::hide)
.dontShowAgainId(key, preferences)
.show(); .show();
} else { } else {
confirmPaymentStarted(); 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.BitsquareApp;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.Restrictions; import io.bitsquare.btc.Restrictions;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.funds.FundsView; import io.bitsquare.gui.main.funds.FundsView;
import io.bitsquare.gui.main.funds.transactions.TransactionsView; 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.overlays.popups.Popup;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView; 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 = addButtonAfterGroup(gridPane, ++gridRow, "Withdraw to external wallet");
withdrawButton.setOnAction(e -> reviewWithdrawal()); withdrawButton.setOnAction(e -> reviewWithdrawal());
String key = "tradeCompleteInfo"; if (BitsquareApp.DEV_MODE) {
if (BitsquareApp.DEV_MODE)
withdrawAddressTextField.setText("mi8k5f9L972VgDaT4LgjAhriC9hHEPL7EW"); withdrawAddressTextField.setText("mi8k5f9L972VgDaT4LgjAhriC9hHEPL7EW");
else } else {
new Popup().headLine("Trade completed") String key = "tradeCompleted" + trade.getId();
.instruction("You can withdraw your funds now to your external Bitcoin wallet.") if (preferences.showAgain(key))
.dontShowAgainId(key, preferences) new Notification().headLine("Trade completed")
.show(); .notification("You can withdraw your funds now to your external Bitcoin wallet.")
.onClose(() -> preferences.dontShowAgain(key, true))
.autoClose()
.show();
}
} }
private void doWithdrawal() { private void doWithdrawal() {
@ -131,8 +136,9 @@ public class BuyerStep5View extends TradeStepView {
model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(), model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(),
() -> { () -> {
String key = "tradeCompleteWithdrawCompletedInfo"; String key = "tradeCompleteWithdrawCompletedInfo";
new Popup().headLine("Withdrawal completed").instruction("Your completed trades are stored under \"Portfolio/History\".\n" + new Popup().headLine("Withdrawal completed")
"You can review all your bitcoin transactions under \"Funds/Transactions\"") .feedback("Your completed trades are stored under \"Portfolio/History\".\n" +
"You can review all your bitcoin transactions under \"Funds/Transactions\"")
.actionButtonText("Go to \"Transactions\"") .actionButtonText("Go to \"Transactions\"")
.onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class)) .onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class))
.dontShowAgainId(key, preferences) .dontShowAgainId(key, preferences)
@ -151,45 +157,54 @@ public class BuyerStep5View extends TradeStepView {
private void reviewWithdrawal() { private void reviewWithdrawal() {
Coin senderAmount = trade.getPayoutAmount(); Coin senderAmount = trade.getPayoutAmount();
WalletService walletService = model.dataModel.walletService; 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(); String toAddresses = withdrawAddressTextField.getText();
if (Restrictions.isAboveFixedTxFeeAndDust(senderAmount)) { // TODO at some error situation it can be tha the funds are already paid out and we get stuck here
try { // need handling to remove the trade (planned for next release)
Coin requiredFee = walletService.getRequiredFee(fromAddresses, toAddresses, senderAmount, null); Coin balance = walletService.getBalanceForAddress(fromAddressesEntry.getAddress());
Coin receiverAmount = senderAmount.subtract(requiredFee); if (balance.isZero()) {
if (BitsquareApp.DEV_MODE) { new Popup().warning("Your funds have already been withdrawn.\nPlease check the transaction history.").show();
doWithdrawal(); model.dataModel.tradeManager.addTradeToClosedTrades(trade);
} 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());
}
} else { } else {
new Popup() if (Restrictions.isAboveFixedTxFeeAndDust(senderAmount) && !toAddresses.isEmpty()) {
.warning("The amount to transfer is lower than the transaction fee and the min. possible tx value (dust).") try {
.show(); 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.common.util.Tuple3;
import io.bitsquare.gui.components.TextFieldWithCopyIcon; import io.bitsquare.gui.components.TextFieldWithCopyIcon;
import io.bitsquare.gui.components.TitledGroupBg; 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.overlays.popups.Popup;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
@ -49,7 +48,6 @@ public class SellerStep3View extends TradeStepView {
private Label statusLabel; private Label statusLabel;
private ProgressIndicator statusProgressIndicator; private ProgressIndicator statusProgressIndicator;
private Subscription tradeStatePropertySubscription; private Subscription tradeStatePropertySubscription;
private Overlay attentionRequiredPopup;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -67,34 +65,33 @@ public class SellerStep3View extends TradeStepView {
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> { tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) { if (state == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) {
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData(); PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
String key = "confirmPaymentPopup"; String key = "confirmPayment" + trade.getId();
if (attentionRequiredPopup == null && !BitsquareApp.DEV_MODE) { String message;
String message; String tradeAmountWithCode = model.formatter.formatFiatWithCode(trade.getTradeVolume());
String tradeAmountWithCode = model.formatter.formatFiatWithCode(trade.getTradeVolume()); String currencyName = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode());
String currencyName = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()); if (paymentAccountContractData instanceof BlockChainAccountContractData) {
if (paymentAccountContractData instanceof BlockChainAccountContractData) { String address = ((BlockChainAccountContractData) paymentAccountContractData).getAddress();
String address = ((BlockChainAccountContractData) paymentAccountContractData).getAddress(); message = "Your trading partner has confirmed that he initiated the " + currencyName + " payment.\n\n" +
message = "Your trading partner has confirmed that he initiated the " + currencyName + " payment.\n\n" + "Please check on your favorite " + currencyName +
"Please check on your favorite " + currencyName + " blockchain explorer if the transaction to your receiving address\n" +
" blockchain explorer if the transaction to your receiving address\n" + "" + address + "\n" +
"" + address + "\n" + "has already sufficient blockchain confirmations.\n" +
"has already sufficient blockchain confirmations.\n" + "The payment amount has to be " + tradeAmountWithCode + "\n\n" +
"The payment amount has to be " + tradeAmountWithCode + "\n\n" + "You can copy & paste your " + currencyName + " address from the main screen after " +
"You can copy & paste your " + currencyName + " address from the main screen after " + "closing that popup.";
"closing that popup."; } else {
} else { message = "Your trading partner has confirmed that he initiated the " + currencyName + " payment.\n\n" +
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 " +
"Please go to your online banking web page and check if you have received " + tradeAmountWithCode + " from the bitcoin buyer.\n\n" +
tradeAmountWithCode + " from the bitcoin buyer.\n\n" + "The reference text of the transaction is: \"" + trade.getShortId() + "\"";
"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();
} }
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) { } else if (state == Trade.State.SELLER_CONFIRMED_FIAT_PAYMENT_RECEIPT) {
showStatusInfo(); showStatusInfo();
statusLabel.setText("Sending confirmation..."); 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 " + "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.") "to the bitcoin buyer and the security deposit will be refunded.")
.width(700) .width(700)
.dontShowAgainId(key, preferences)
.actionButtonText("Yes, I have received the payment") .actionButtonText("Yes, I have received the payment")
.closeButtonText("Cancel")
.onAction(this::confirmPaymentReceived) .onAction(this::confirmPaymentReceived)
.closeButtonText("Cancel")
.dontShowAgainId(key, preferences)
.show(); .show();
} else { } else {
confirmPaymentReceived(); confirmPaymentReceived();

View file

@ -44,7 +44,6 @@ import javafx.util.Callback;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Locale;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
@ -54,7 +53,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
// not supported yet // not supported yet
//private ComboBox<String> btcDenominationComboBox; //private ComboBox<String> btcDenominationComboBox;
private ComboBox<BlockChainExplorer> blockChainExplorerComboBox; private ComboBox<BlockChainExplorer> blockChainExplorerComboBox;
private ComboBox<String> userLanguageComboBox; // private ComboBox<String> userLanguageComboBox;
private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox; private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox;
private CheckBox useAnimationsCheckBox, autoSelectArbitratorsCheckBox; private CheckBox useAnimationsCheckBox, autoSelectArbitratorsCheckBox;
@ -275,11 +274,11 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
} }
private void initializeOtherOptions() { 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); 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; // 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; 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 // 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.getSelectionModel().select(getBtcDenomination());
btcDenominationComboBox.setOnAction(e -> onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/ btcDenominationComboBox.setOnAction(e -> onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/
userLanguageComboBox.setItems(languageCodes); /* userLanguageComboBox.setItems(languageCodes);
userLanguageComboBox.getSelectionModel().select(preferences.getPreferredLocale().getLanguage()); userLanguageComboBox.getSelectionModel().select(preferences.getPreferredLocale().getLanguage());
userLanguageComboBox.setConverter(new StringConverter<String>() { userLanguageComboBox.setConverter(new StringConverter<String>() {
@Override @Override
@ -368,7 +367,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
userLanguageComboBox.setOnAction(e -> { userLanguageComboBox.setOnAction(e -> {
String code = userLanguageComboBox.getSelectionModel().getSelectedItem(); String code = userLanguageComboBox.getSelectionModel().getSelectedItem();
preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry())); preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry()));
}); });*/
blockChainExplorerComboBox.setItems(blockExplorers); blockChainExplorerComboBox.setItems(blockExplorers);
@ -410,7 +409,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
private void deactivateOtherOptions() { private void deactivateOtherOptions() {
//btcDenominationComboBox.setOnAction(null); //btcDenominationComboBox.setOnAction(null);
userLanguageComboBox.setOnAction(null); // userLanguageComboBox.setOnAction(null);
blockChainExplorerComboBox.setOnAction(null); blockChainExplorerComboBox.setOnAction(null);
// transactionFeeInputTextField.textProperty().unbind(); // transactionFeeInputTextField.textProperty().unbind();
/// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener); /// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);

View file

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