Add trading popups

This commit is contained in:
Manfred Karrer 2016-03-02 20:05:24 +01:00
parent 29aac699f8
commit 1958de2be9
38 changed files with 472 additions and 298 deletions

View file

@ -217,7 +217,7 @@ public class WalletService {
// reduce for mainnet testing
// TODO revert later when tested enough
FeePolicy.setSecurityDeposit(Coin.parseCoin("0.01")); // 4 EUR @ 400 EUR/BTC
Restrictions.setMaxTradeAmount(Coin.parseCoin("0.01")); // 4 EUR @ 400 EUR/BTC
Restrictions.setMaxTradeAmount(Coin.parseCoin("0.1")); // 40 EUR @ 400 EUR/BTC
// Checkpoints are block headers that ship inside our app: for a new user, we pick the last header
// in the checkpoints file and then download the rest from the network. It makes things much faster.

View file

@ -1,29 +0,0 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.user;
public class PopupId {
// We don't use an enum because it would break updates if we add a new item in a new version
public static final String TRADE_WALLET = "tradeWallet";
public static final String SEND_PAYMENT_INFO = "sendPaymentInfo";
public static final String PAYMENT_SENT = "paymentSent";
public static final String PAYMENT_RECEIVED = "paymentReceived";
}

View file

@ -96,8 +96,6 @@ public final class Preferences implements Persistable {
private final ArrayList<CryptoCurrency> cryptoCurrencies;
private BlockChainExplorer blockChainExplorerMainNet;
private BlockChainExplorer blockChainExplorerTestNet;
private boolean showNotifications = true;
private boolean showInstructions = true;
private String backupDirectory;
private boolean autoSelectArbitrators = true;
private final Map<String, Boolean> showAgainMap;
@ -146,8 +144,6 @@ public final class Preferences implements Persistable {
if (blockChainExplorerMainNet == null)
setBlockChainExplorerTestNet(blockChainExplorersMainNet.get(0));
showNotifications = persisted.getShowNotifications();
showInstructions = persisted.getShowInstructions();
backupDirectory = persisted.getBackupDirectory();
autoSelectArbitrators = persisted.getAutoSelectArbitrators();
showAgainMap = persisted.getShowAgainMap();
@ -175,11 +171,6 @@ public final class Preferences implements Persistable {
setBlockChainExplorerMainNet(blockChainExplorersMainNet.get(0));
showAgainMap = new HashMap<>();
showAgainMap.put(PopupId.TRADE_WALLET, true);
showAgainMap.put(PopupId.SEND_PAYMENT_INFO, true);
showAgainMap.put(PopupId.PAYMENT_SENT, true);
showAgainMap.put(PopupId.PAYMENT_RECEIVED, true);
preferredLocale = getDefaultLocale();
preferredTradeCurrency = getDefaultTradeCurrency();
@ -219,8 +210,22 @@ public final class Preferences implements Persistable {
tradeCurrenciesAsObservable.addAll(cryptoCurrencies);
}
public void dontShowAgain(String id) {
showAgainMap.put(id, false);
public void dontShowAgain(String key) {
showAgainMap.put(key, false);
storage.queueUpForSave(2000);
}
public void resetDontShowAgainForType() {
showAgainMap.clear();
storage.queueUpForSave(2000);
}
public void removeDontShowAgainForType(String type) {
showAgainMap.keySet().stream().forEach(key -> {
if (key.startsWith(type + "_"))
showAgainMap.put(key, true);
});
storage.queueUpForSave(2000);
}
@ -317,16 +322,6 @@ public final class Preferences implements Persistable {
setBlockChainExplorerTestNet(blockChainExplorer);
}
public void setShowNotifications(boolean showNotifications) {
this.showNotifications = showNotifications;
storage.queueUpForSave(2000);
}
public void setShowInstructions(boolean showInstructions) {
this.showInstructions = showInstructions;
storage.queueUpForSave(2000);
}
public void setTacAccepted(boolean tacAccepted) {
this.tacAccepted = tacAccepted;
storage.queueUpForSave();
@ -430,14 +425,6 @@ public final class Preferences implements Persistable {
return blockChainExplorersTestNet;
}
public boolean getShowNotifications() {
return showNotifications;
}
public boolean getShowInstructions() {
return showInstructions;
}
public String getBackupDirectory() {
return backupDirectory;
}

View file

@ -973,5 +973,12 @@ textfield */
-fx-background-radius: 5 5 5 5;
-fx-background-insets: 5 5 20 20;
-fx-effect: dropshadow(gaussian, #000000, 15, 0, 2, 2);
}
.popup-icon-information {
-fx-text-fill: #ddd;
}
.popup-icon-warning {
-fx-text-fill: #dd6900;
}

View file

@ -225,7 +225,7 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
private void onRevoke() {
if (model.isBootstrapped()) {
model.onRevoke(
() -> new Popup().information("You have successfully removed your arbitrator from the P2P network.").show(),
() -> new Popup().feedback("You have successfully removed your arbitrator from the P2P network.").show(),
(errorMessage) -> new Popup().error("Could not remove arbitrator.\nError message: " + errorMessage).show());
} else {
new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +
@ -236,7 +236,7 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
private void onRegister() {
if (model.isBootstrapped()) {
model.onRegister(
() -> new Popup().information("You have successfully registered your arbitrator to the P2P network.").show(),
() -> new Popup().feedback("You have successfully registered your arbitrator to the P2P network.").show(),
(errorMessage) -> new Popup().error("Could not register arbitrator.\nError message: " + errorMessage).show());
} else {
new Popup().warning("You need to wait until your client is bootstrapped in the network.\n" +

View file

@ -126,7 +126,8 @@ public class AltCoinAccountsView extends ActivatableViewAndModel<GridPane, AltCo
model.onSaveNewAccount(paymentAccount);
removeNewAccountForm();
} else {
new Popup().error("That account name is already used in a saved account. \nPlease use another name.").show();
new Popup().warning("That account name is already used in a saved account.\n" +
"Please use another name.").show();
}
}
@ -136,6 +137,8 @@ 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();

View file

@ -104,7 +104,7 @@ public class BackupView extends ActivatableView<GridPane, Void> {
String destination = Paths.get(backupDirectory, "bitsquare_backup_" + dateString).toString();
FileUtils.copyDirectory(new File(environment.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY)),
new File(destination));
new Popup().information("Backup successfully saved at:\n" + destination).show();
new Popup().feedback("Backup successfully saved at:\n" + destination).show();
} catch (IOException e1) {
e1.printStackTrace();
log.error(e1.getMessage());

View file

@ -128,7 +128,8 @@ public class FiatAccountsView extends ActivatableViewAndModel<GridPane, FiatAcco
model.onSaveNewAccount(paymentAccount);
removeNewAccountForm();
} else {
new Popup().error("That account name is already used in a saved account. \nPlease use another name.").show();
new Popup().warning("That account name is already used in a saved account.\n" +
"Please use another name.").show();
}
}
@ -138,6 +139,8 @@ 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();

View file

@ -113,14 +113,14 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
wallet.decrypt(aesKey);
tradeWalletService.setAesKey(null);
new Popup()
.information("Wallet successfully decrypted and password protection removed.")
.feedback("Wallet successfully decrypted and password protection removed.")
.show();
passwordField.setText("");
repeatedPasswordField.setText("");
} else {
new Popup()
.headLine("Wrong password")
.message("Please try entering your password again, carefully checking for typos or spelling errors.")
.warning("You entered the wrong password.\n\n" +
"Please try entering your password again, carefully checking for typos or spelling errors.")
.show();
}
} else {
@ -128,7 +128,7 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
// we save the key for the trade wallet as we don't require passwords here
tradeWalletService.setAesKey(aesKey);
new Popup()
.information("Wallet successfully encrypted and password protection enabled.")
.feedback("Wallet successfully encrypted and password protection enabled.")
.show();
passwordField.setText("");
repeatedPasswordField.setText("");

View file

@ -198,17 +198,17 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
Wallet wallet = walletService.getWallet();
if (wallet.getBalance(Wallet.BalanceType.AVAILABLE).value > 0) {
new Popup()
.headLine("Wallet is not empty")
.warning("You must empty this wallet out before attempting to restore an older one, as mixing wallets " +
.warning("Your bitcoin wallet is not empty.\n\n" +
"You must empty this wallet before attempting to restore an older one, as mixing wallets " +
"together can lead to invalidated backups.\n\n" +
"Please finalize your trades, close all your open offers and go to the Funds section to withdraw your bitcoin.\n" +
"In case you cannot access your bitcoin you can use the emergency tool to empty the wallet.\n" +
"To open that emergency tool press cmd + e.")
"To open that emergency tool press \"cmd + e\".")
.show();
} else if (wallet.isEncrypted()) {
new Popup()
.headLine("Wallet is encrypted")
.information("After restore, the wallet will no longer be encrypted and you must set a new password.")
.warning("Your bitcoin wallet is encrypted.\n\n" +
"After restore, the wallet will no longer be encrypted and you must set a new password.")
.closeButtonText("I understand")
.onClose(() -> doRestore()).show();
} else {
@ -225,7 +225,7 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
log.debug("Wallet restored with seed words");
new Popup()
.information("Wallet restored successfully with the new seed words.\n\n" +
.feedback("Wallet restored successfully with the new seed words.\n\n" +
"You need to shut down and restart the application.")
.closeButtonText("Shut down")
.onClose(() -> BitsquareApp.shutDownHandler.run()).show();
@ -239,8 +239,8 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
throwable -> UserThread.execute(() -> {
log.error(throwable.getMessage());
new Popup()
.headLine("Wrong password")
.warning("Please try entering your password again, carefully checking for typos or spelling errors.")
.warning("You entered the wrong password.\n\n" +
"Please try entering your password again, carefully checking for typos or spelling errors.")
.show();
new Popup()

View file

@ -289,7 +289,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
}
}
} else {
new Popup().error("You cannot send more then 3 attachments in one message.").show();
new Popup().warning("You cannot send more then 3 attachments in one message.").show();
}
}

View file

@ -18,6 +18,7 @@
package io.bitsquare.gui.main.funds;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.Activatable;
import io.bitsquare.gui.common.view.*;
@ -26,7 +27,6 @@ import io.bitsquare.gui.main.funds.reserved.ReservedView;
import io.bitsquare.gui.main.funds.transactions.TransactionsView;
import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
import io.bitsquare.gui.main.popups.Popup;
import io.bitsquare.user.PopupId;
import io.bitsquare.user.Preferences;
import javafx.beans.value.ChangeListener;
import javafx.fxml.FXML;
@ -85,15 +85,20 @@ public class FundsView extends ActivatableViewAndModel<TabPane, Activatable> {
else if (root.getSelectionModel().getSelectedItem() == transactionsTab)
navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class);
String key = PopupId.TRADE_WALLET;
String text = "Bitsquare does not use a single application wallet, but dedicated wallets for every trade.\n" +
"Funding of the wallet will be done when needed, for instance when you create or take an offer.\n" +
"Withdrawing funds can be done after a trade is completed.\n" +
"Dedicated wallets help protect user privacy and prevent leaking information of previous trades to other" +
"traders.\n\n" +
"For more background information please see the Bitsquare FAQ on our web page.";
String key = "tradeWalletInfoAtFunds";
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE)
new Popup().information(text).dontShowAgainId(key, preferences).show();
new Popup().backgroundInfo("Bitsquare does not use a single application wallet, but dedicated wallets for every trade.\n\n" +
"Funding of the wallet will be done when needed, for instance when you create or take an offer.\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" +
"traders.")
.closeButtonText("I want to learn more")
.onClose(() -> Utilities.openWebPage("https://bitsquare.io/faq"))
.actionButtonText("I understand")
.onAction(() -> {
})
.dontShowAgainId(key, preferences)
.show();
}
@Override

View file

@ -188,14 +188,16 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
if (BitsquareApp.DEV_MODE) {
doWithdraw(receiverAmount, callback);
} else {
new Popup().headLine("Confirm your withdrawal request")
.message("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" +
new Popup().headLine("Confirm withdrawal request")
.confirmation("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" +
"From address: " + withdrawFromTextField.getText() + "\n" +
"To receiving address: " + withdrawToTextField.getText() + ".\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(() -> doWithdraw(receiverAmount, callback))
.closeButtonText("Cancel")
.show();
}
@ -323,10 +325,10 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
reset();
updateList();
} catch (AddressFormatException e) {
new Popup().error("The address is not correct. Please check the address format.").show();
new Popup().warning("The address is not correct. Please check the address format.").show();
} catch (InsufficientMoneyException e) {
log.warn(e.getMessage());
new Popup().error("You don't have enough fund in your wallet.").show();
new Popup().warning("You don't have enough fund in your wallet.").show();
}
}

View file

@ -33,7 +33,9 @@ public class Help {
public static void openWindow(HelpId id) {
try {
Utilities.openWebPage("https://github.com/bitsquare/bitsquare/wiki/User-Guide");
// TODO create user guide
Utilities.openWebPage("http://bitsquare.io/faq");
// Utilities.openWebPage("https://github.com/bitsquare/bitsquare/wiki/User-Guide");
} catch (Exception e) {
log.error(e.getMessage());
new Popup().warning("Opening browser failed. Please check your internet " +

View file

@ -37,6 +37,4 @@ public enum HelpId {
SETUP_FIAT_ACCOUNT,
MANAGE_FIAT_ACCOUNT,
PAY_ACCOUNT_FEE
}

View file

@ -1,5 +1,7 @@
package io.bitsquare.gui.main.notifications;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.main.popups.Popup;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
@ -19,6 +21,8 @@ import org.slf4j.LoggerFactory;
public class Notification extends Popup {
private static final Logger log = LoggerFactory.getLogger(Notification.class);
private boolean hasBeenDisplayed;
private boolean autoClose;
private Timer autoCloseTimer;
public Notification() {
width = 345; // 320 visible bg because of insets
@ -33,6 +37,10 @@ public class Notification extends Popup {
return headLine("Notification for trade with ID " + tradeId);
}
public Notification notification(String message) {
return (Notification) super.notification(message);
}
public Notification disputeHeadLine(String tradeId) {
return headLine("Support ticket for trade with ID " + tradeId);
}
@ -52,13 +60,26 @@ public class Notification extends Popup {
hasBeenDisplayed = true;
}
public Notification autoClose() {
autoClose = true;
return this;
}
@Override
public void display() {
super.display();
if (autoClose && autoCloseTimer == null)
autoCloseTimer = UserThread.runAfter(this::hide, 5);
}
@Override
protected void animateHide(Runnable onFinishedHandler) {
if (autoCloseTimer != null) {
autoCloseTimer.stop();
autoCloseTimer = null;
}
if (NotificationCenter.useAnimations) {
double duration = 400;
Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1);

View file

@ -50,7 +50,6 @@ public class NotificationCenter {
private final TradeManager tradeManager;
private final DisputeManager disputeManager;
private final Preferences preferences;
private final Navigation navigation;
private final Map<String, Subscription> disputeStateSubscriptionsMap = new HashMap<>();
@ -67,7 +66,6 @@ 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);

View file

@ -29,6 +29,7 @@ import io.bitsquare.btc.pricefeed.PriceFeed;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.notifications.Notification;
import io.bitsquare.gui.main.popups.Popup;
import io.bitsquare.gui.main.popups.WalletPasswordPopup;
import io.bitsquare.gui.util.BSFormatter;
@ -381,8 +382,15 @@ class CreateOfferDataModel extends ActivatableDataModel {
private void updateBalance(Coin balance) {
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
if (isWalletFunded.get())
if (isWalletFunded.get()) {
walletService.removeBalanceListener(balanceListener);
new Notification()
.headLine("Trading wallet update")
.notification("Your trading wallet is sufficiently funded.\n" +
"Amount: " + formatter.formatCoinWithCode(totalToPayAsCoin.get()))
.autoClose()
.show();
}
}
public Coin getOfferFeeAsCoin() {

View file

@ -23,6 +23,7 @@ import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
import io.bitsquare.gui.common.view.FxmlView;
@ -111,6 +112,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private EventHandler<ActionEvent> currencyComboBoxSelectionHandler;
private int gridRow = 0;
private final Preferences preferences;
private ChangeListener<String> tradeCurrencyCodeListener;
///////////////////////////////////////////////////////////////////////////////////////////
@ -171,6 +173,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
protected void deactivate() {
removeBindings();
removeListeners();
if (balanceTextField != null)
balanceTextField.cleanup();
}
@ -188,7 +192,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
createOfferButton.setId("buy-button-big");
nextButton.setId("buy-button");
createOfferButton.setText("Place offer for buying bitcoin");
createOfferButton.setText("Review place offer for buying bitcoin");
iconView.setId("image-buy-white");
} else {
imageView.setId("image-sell-large");
@ -197,7 +201,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
createOfferButton.setId("sell-button-big");
nextButton.setId("sell-button");
createOfferButton.setText("Place offer for selling bitcoin");
createOfferButton.setText("Review place offer for selling bitcoin");
iconView.setId("image-sell-white");
}
}
@ -247,25 +251,44 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
}
private void onShowFundsScreen() {
amountTextField.setMouseTransparent(true);
minAmountTextField.setMouseTransparent(true);
priceTextField.setMouseTransparent(true);
volumeTextField.setMouseTransparent(true);
currencyComboBox.setMouseTransparent(true);
paymentAccountsComboBox.setMouseTransparent(true);
if (!BitsquareApp.DEV_MODE) {
String id = "tradeWalletInfoPopup";
String id = "securityDepositInfo";
if (model.dataModel.getPreferences().showAgain(id)) {
new Popup().information("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" +
"It will be refunded to you after the trade has successfully completed.\n\n" +
"You need to pay in the exact amount displayed to you from your external bitcoin wallet into the " +
"Bitsquare trade wallet.\n" +
"The amount is the sum of the security deposit, the trading fee and " +
"the bitcoin mining fee.\n" +
"You can see the details when you move the mouse over the question mark.\n\n" +
"Important notice!\n" +
"Please take care that you use a mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + " when you transfer bitcoin from your external " +
"wallet to ensure the trade transactions will get into the blockchain.\n" +
"A too low mining fee might result in a delayed trade and will be rejected!")
.closeButtonText("I understand")
"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(() -> {
})
.dontShowAgainId(id, preferences)
.show();
}
id = "createOfferFundWalletInfo";
if (model.dataModel.getPreferences().showAgain(id)) {
String tradeAmountText = model.isSellOffer() ? "the trade amount, " : "";
new Popup().headLine("Fund your trading wallet").instruction("You need to pay in " +
model.totalToPay.get() + " to your local Bitsquare trading wallet.\n" +
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
"the bitcoin mining fee.\n\n" +
"Please send from your external Bitcoin wallet the exact amount to the address: " +
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" +
"You can see the status of your incoming payment and all the details in the screen below.")
.dontShowAgainId(id, preferences)
.show();
model.dataModel.getPreferences().dontShowAgain(id);
}
}
@ -283,12 +306,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
addressTextField.setVisible(true);
balanceLabel.setVisible(true);
balanceTextField.setVisible(true);
createOfferButton.setVisible(true);
cancelButton2.setVisible(true);
//root.requestFocus();
setupTotalToPayInfoIconLabel();
model.onShowPayFundsScreen();
}
private void onPaymentAccountsComboBoxSelected() {
@ -366,7 +388,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
volumeTextField.validationResultProperty().bind(model.volumeValidationResult);
// buttons
createOfferButton.visibleProperty().bind(model.isPlaceOfferButtonVisible);
createOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled);
placeOfferSpinnerInfoLabel.visibleProperty().bind(model.isPlaceOfferSpinnerVisible);
@ -399,7 +420,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
minAmountTextField.validationResultProperty().unbind();
priceTextField.validationResultProperty().unbind();
volumeTextField.validationResultProperty().unbind();
createOfferButton.visibleProperty().unbind();
createOfferButton.disableProperty().unbind();
placeOfferSpinnerInfoLabel.visibleProperty().unbind();
currencyComboBox.managedProperty().unbind();
@ -487,6 +507,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
paymentAccountsComboBoxSelectionHandler = e -> onPaymentAccountsComboBoxSelected();
currencyComboBoxSelectionHandler = e -> onCurrencyComboBoxSelected();
tradeCurrencyCodeListener = (observable, oldValue, newValue) -> {
priceTextField.clear();
volumeTextField.clear();
};
showTransactionPublishedScreen = (o, oldValue, newValue) -> {
if (BitsquareApp.DEV_MODE) {
newValue = false;
@ -498,7 +523,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
FxTimer.runLater(Duration.ofMillis(100),
() -> {
new Popup().headLine(BSResources.get("createOffer.success.headline"))
.message(BSResources.get("createOffer.success.info"))
.feedback(BSResources.get("createOffer.success.info"))
.actionButtonText("Go to \"My open offers\"")
.onAction(() -> {
close();
@ -515,6 +540,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
}
private void addListeners() {
model.tradeCurrencyCode.addListener(tradeCurrencyCodeListener);
// focus out
amountTextField.focusedProperty().addListener(amountFocusedListener);
minAmountTextField.focusedProperty().addListener(minAmountFocusedListener);
@ -537,6 +564,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
}
private void removeListeners() {
model.tradeCurrencyCode.removeListener(tradeCurrencyCodeListener);
// focus out
amountTextField.focusedProperty().removeListener(amountFocusedListener);
minAmountTextField.focusedProperty().removeListener(minAmountFocusedListener);
@ -644,6 +673,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, BSResources.get("createOffer.amountPriceBox.next"), BSResources.get("shared.cancel"));
nextButton = tuple.first;
nextButton.disableProperty().bind(model.isNextButtonDisabled);
//UserThread.runAfter(() -> nextButton.requestFocus(), 100, TimeUnit.MILLISECONDS);
cancelButton1 = tuple.second;
cancelButton1.setDefaultButton(false);
@ -697,7 +727,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
createOfferButton.setOnAction(e -> onPlaceOffer());
createOfferButton.setMinHeight(40);
createOfferButton.setPadding(new Insets(0, 20, 0, 20));
placeOfferSpinner = placeOfferTuple.second;
placeOfferSpinner.setPrefSize(18, 18);
placeOfferSpinnerInfoLabel = placeOfferTuple.third;

View file

@ -18,7 +18,6 @@
package io.bitsquare.gui.main.offer.createoffer;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter;
@ -38,7 +37,6 @@ import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import javax.inject.Inject;
import java.util.List;
import static javafx.beans.binding.Bindings.createStringBinding;
@ -66,8 +64,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
final StringProperty tradeCurrencyCode = new SimpleStringProperty();
final StringProperty placeOfferSpinnerInfoText = new SimpleStringProperty();
final BooleanProperty isPlaceOfferButtonVisible = new SimpleBooleanProperty(false);
final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true);
final BooleanProperty isNextButtonDisabled = new SimpleBooleanProperty(true);
final BooleanProperty isPlaceOfferSpinnerVisible = new SimpleBooleanProperty(false);
final BooleanProperty showWarningAdjustedVolume = new SimpleBooleanProperty();
final BooleanProperty showWarningInvalidFiatDecimalPlaces = new SimpleBooleanProperty();
@ -246,7 +244,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
};
requestPlaceOfferSuccessListener = (ov, oldValue, newValue) -> {
if (newValue) {
isPlaceOfferButtonVisible.set(!newValue);
isPlaceOfferButtonDisabled.set(newValue);
isPlaceOfferSpinnerVisible.set(false);
placeOfferSpinnerInfoText.set("");
}
@ -336,10 +334,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.onPlaceOffer(offer, transaction -> requestPlaceOfferSuccess.set(true));
}
void onShowPayFundsScreen() {
isPlaceOfferButtonVisible.set(true);
}
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
dataModel.onPaymentAccountSelected(paymentAccount);
}
@ -563,18 +557,15 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
private void updateButtonDisableState() {
log.debug("updateButtonDisableState");
isPlaceOfferButtonDisabled.set(
!(isBtcInputValid(amount.get()).isValid &&
isBtcInputValid(minAmount.get()).isValid &&
isFiatInputValid(price.get()).isValid &&
isFiatInputValid(volume.get()).isValid &&
dataModel.isMinAmountLessOrEqualAmount() &&
boolean inputDataValid = isBtcInputValid(amount.get()).isValid &&
isBtcInputValid(minAmount.get()).isValid &&
isFiatInputValid(price.get()).isValid &&
isFiatInputValid(volume.get()).isValid &&
dataModel.isMinAmountLessOrEqualAmount();
isNextButtonDisabled.set(!inputDataValid);
isPlaceOfferButtonDisabled.set(!(inputDataValid &&
dataModel.isWalletFunded.get() &&
dataModel.isFeeFromFundingTxSufficient())
);
}
public List<Arbitrator> getArbitrators() {
return dataModel.getArbitrators();
}
}

View file

@ -253,17 +253,18 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
private void onCreateOffer() {
if (!model.hasPaymentAccount()) {
showWarning("You don't have setup a payment account yet.",
"You need to setup your payment account before you can trade.\nDo you want to do this now?", FiatAccountsView.class);
openPopupForMissingAccountSetup("You have not setup a payment account",
"You need to setup a national currency or crypto currency account before you can create an offer.\n" +
"Do you want to setup an account?", FiatAccountsView.class, "\"Account\"");
} else if (!model.hasPaymentAccountForCurrency()) {
showWarning("You don't have a payment account for the currency:\n" +
openPopupForMissingAccountSetup("You don't have a payment account for the currency:\n" +
model.getSelectedTradeCurrency().getCodeAndName(),
"You need to setup a payment account for the selected currency to be able to trade in that currency.\n" +
"Do you want to do this now?", FiatAccountsView.class);
"Do you want to do this now?", FiatAccountsView.class, "\"Account\"");
} else if (!model.hasAcceptedArbitrators()) {
showWarning("You don't have an arbitrator selected.",
openPopupForMissingAccountSetup("You don't have an arbitrator selected.",
"You need to setup at least one arbitrator to be able to trade.\n" +
"Do you want to do this now?", ArbitratorSelectionView.class);
"Do you want to do this now?", ArbitratorSelectionView.class, "\"Arbitrator selection\"");
} else {
createOfferButton.setDisable(true);
offerActionHandler.onCreateOffer(model.getSelectedTradeCurrency());
@ -272,19 +273,19 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
private void onShowInfo(boolean isPaymentAccountValidForOffer, boolean hasMatchingArbitrator, boolean hasSameProtocolVersion) {
if (!hasMatchingArbitrator) {
showWarning("You don't have an arbitrator selected.",
openPopupForMissingAccountSetup("You don't have an arbitrator selected.",
"You need to setup at least one arbitrator to be able to trade.\n" +
"Do you want to do this now?", ArbitratorSelectionView.class);
"Do you want to do this now?", ArbitratorSelectionView.class, "\"Arbitrator selection\"");
} else if (!isPaymentAccountValidForOffer) {
showWarning("You don't have a payment account with the payment method required for that offer.",
openPopupForMissingAccountSetup("You don't have a payment account with the payment method required for that offer.",
"You need to setup a payment account with that payment method if you want to take that offer.\n" +
"Do you want to do this now?", FiatAccountsView.class);
"Do you want to do this now?", FiatAccountsView.class, "\"Account\"");
} else if (!hasSameProtocolVersion) {
new Popup().information("That offer requires a different protocol version as the one used in your " +
"version of the software." +
"\n\n" + "Please check if you have the latest version installed, otherwise the user " +
"who created the offer has used an older version.\n" +
"You cannot trade with an incompatible protocol version.")
new Popup().warning("That offer requires a different protocol version as the one used in your " +
"version of the software.\n\n" +
"Please check if you have the latest version installed, otherwise the user " +
"who created the offer has used an older version.\n\n" +
"Users cannot trade with an incompatible trade protocol version.")
.show();
}
}
@ -315,8 +316,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
model.onRemoveOpenOffer(offer,
() -> {
log.debug("Remove offer was successful");
new Popup().information("You can withdraw the funds you paid in from the funds screens.").show();
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
new Popup().instruction("You can withdraw the funds you paid in from the \"Fund/Available for withdrawal\" screen.")
.actionButtonText("Go to \"Funds/Available for withdrawal\"")
.onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class))
.show();
},
(message) -> {
log.error(message);
@ -324,8 +327,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
});
}
private void showWarning(String masthead, String message, Class target) {
new Popup().information(masthead + "\n\n" + message)
private void openPopupForMissingAccountSetup(String headLine, String message, Class target, String targetAsString) {
new Popup().headLine(headLine)
.instruction(message)
.actionButtonText("Go to " + targetAsString)
.onAction(() -> {
navigation.setReturnPath(navigation.getCurrentPath());
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, target);

View file

@ -30,6 +30,7 @@ import io.bitsquare.btc.pricefeed.PriceFeed;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.notifications.Notification;
import io.bitsquare.gui.main.popups.Popup;
import io.bitsquare.gui.main.popups.WalletPasswordPopup;
import io.bitsquare.gui.util.BSFormatter;
@ -324,8 +325,15 @@ 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);
new Notification()
.headLine("Trading wallet update")
.notification("Your trading wallet is sufficiently funded.\n" +
"Amount: " + formatter.formatCoinWithCode(totalToPayAsCoin.get()))
.autoClose()
.show();
}
}
boolean isMinAmountLessOrEqualAmount() {

View file

@ -23,6 +23,7 @@ import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
import io.bitsquare.gui.common.view.FxmlView;
@ -175,11 +176,14 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
errorMessageSubscription = EasyBind.subscribe(model.errorMessage, newValue -> {
if (newValue != null) {
new Popup().error(BSResources.get("takeOffer.error.message", newValue)).onClose(() -> {
errorPopupDisplayed.set(true);
model.resetErrorMessage();
close();
}).show();
new Popup().error(BSResources.get("takeOffer.error.message", model.errorMessage.get()) +
"Please try to restart you application and check your network connection to see if you can resolve the issue.")
.onClose(() -> {
errorPopupDisplayed.set(true);
model.resetErrorMessage();
close();
})
.show();
}
});
@ -220,15 +224,16 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) {
FxTimer.runLater(Duration.ofMillis(100),
() -> {
new Popup().information(BSResources.get("takeOffer.success.info"))
.actionButtonText("Go to \"Portfolio/Open trades\"")
new Popup().headLine(BSResources.get("takeOffer.success.headline"))
.feedback(BSResources.get("takeOffer.success.info"))
.actionButtonText("Go to \"Open trades\"")
.onAction(() -> {
close();
FxTimer.runLater(Duration.ofMillis(100),
() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)
);
})
.onClose(() -> close())
.onClose(this::close)
.show();
}
);
@ -293,6 +298,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
showCheckAvailabilityPopupSubscription.unsubscribe();
model.dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
if (balanceTextField != null)
balanceTextField.cleanup();
}
@ -310,7 +317,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
directionLabel.setId("direction-icon-label-buy");
takeOfferButton.setId("buy-button-big");
takeOfferButton.setText("Take offer for buying bitcoin");
takeOfferButton.setText("Review take offer for buying bitcoin");
nextButton.setId("buy-button");
iconView.setId("image-buy-white");
} else {
@ -319,7 +326,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
takeOfferButton.setId("sell-button-big");
nextButton.setId("sell-button");
takeOfferButton.setText("Take offer for selling bitcoin");
takeOfferButton.setText("Review take offer for selling bitcoin");
iconView.setId("image-sell-white");
}
@ -390,24 +397,40 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
}
private void onShowPayFundsScreen() {
amountTextField.setMouseTransparent(true);
priceTextField.setMouseTransparent(true);
volumeTextField.setMouseTransparent(true);
if (!BitsquareApp.DEV_MODE) {
String id = "tradeWalletInfoPopup";
String id = "securityDepositInfo";
if (model.dataModel.getPreferences().showAgain(id)) {
new Popup().information("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
"The deposit will be refunded to you after the trade has successfully completed.\n\n" +
"You need to pay in the exact amount displayed to you from your external bitcoin wallet into the " +
"Bitsquare trade wallet.\n" +
"The amount is the sum of the trade amount, the security deposit, the trading fee and " +
"the bitcoin mining fee.\n" +
"You can see the details when you move the mouse over the question mark.\n\n" +
"Important notice!\n" +
"Please take care that you use a mining fee of at least " +
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + " when you transfer bitcoin from your external " +
"wallet to ensure the trade transactions will get into the blockchain.\n" +
"A too low mining fee might result in a delayed trade and will be rejected!")
.closeButtonText("I understand")
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(() -> {
})
.dontShowAgainId(id, preferences)
.show();
}
id = "takeOfferFundWalletInfo";
if (model.dataModel.getPreferences().showAgain(id)) {
String tradeAmountText = model.isSeller() ? "the trade amount, " : "";
new Popup().headLine("Fund your trading wallet").instruction("You need to pay in " +
model.totalToPay.get() + " to your local Bitsquare trading wallet.\n" +
"The amount is the sum of " + tradeAmountText + "the security deposit, the trading fee and " +
"the bitcoin mining fee.\n\n" +
"Please send from your external Bitcoin wallet the exact amount to the address: " +
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" +
"You can see the status of your incoming payment and all the details in the screen below.")
.dontShowAgainId(id, preferences)
.show();
model.dataModel.getPreferences().dontShowAgain(id);
}
}
@ -526,6 +549,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, BSResources.get("takeOffer.amountPriceBox.next"), BSResources.get("shared.cancel"));
nextButton = tuple.first;
nextButton.disableProperty().bind(model.isNextButtonDisabled);
//UserThread.runAfter(() -> nextButton.requestFocus(), 100, TimeUnit.MILLISECONDS);
cancelButton1 = tuple.second;
cancelButton1.setDefaultButton(false);
@ -577,10 +601,13 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
Tuple3<Button, ProgressIndicator, Label> takeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, "");
takeOfferButton = takeOfferTuple.first;
takeOfferButton.setVisible(false);
takeOfferButton.setOnAction(e -> onTakeOffer());
takeOfferButton.setOnAction(e -> {
onTakeOffer();
balanceTextField.cleanup();
});
takeOfferButton.setMinHeight(40);
takeOfferButton.setPadding(new Insets(0, 20, 0, 20));
takeOfferSpinner = takeOfferTuple.second;
takeOfferSpinner.setPrefSize(18, 18);
takeOfferSpinnerInfoLabel = takeOfferTuple.third;

View file

@ -71,6 +71,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
final BooleanProperty isOfferAvailable = new SimpleBooleanProperty();
final BooleanProperty isTakeOfferButtonDisabled = new SimpleBooleanProperty(true);
final BooleanProperty isNextButtonDisabled = new SimpleBooleanProperty(true);
final BooleanProperty isTakeOfferSpinnerVisible = new SimpleBooleanProperty();
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
@ -340,13 +341,14 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
}
private void updateButtonDisableState() {
isTakeOfferButtonDisabled.set(!(isBtcInputValid(amount.get()).isValid
&& dataModel.isMinAmountLessOrEqualAmount()
&& !dataModel.isAmountLargerThanOfferAmount()
&& dataModel.isWalletFunded.get()
&& !takeOfferRequested
&& dataModel.isFeeFromFundingTxSufficient())
);
boolean inputDataValid = isBtcInputValid(amount.get()).isValid
&& dataModel.isMinAmountLessOrEqualAmount()
&& !dataModel.isAmountLargerThanOfferAmount();
isNextButtonDisabled.set(!inputDataValid);
isTakeOfferButtonDisabled.set(!(inputDataValid
&& dataModel.isWalletFunded.get()
&& !takeOfferRequested
&& dataModel.isFeeFromFundingTxSufficient()));
}
@ -401,8 +403,9 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
if (connection.getPeersNodeAddressOptional().isPresent() &&
connection.getPeersNodeAddressOptional().get().equals(offer.getOffererNodeAddress()))
offerWarning.set("You lost connection to the offerer.\n" +
"He might have gone offline or has closed the connection to you because of " +
"many other connections.");
"He might have gone offline or has closed the connection to you because of too " +
"many open connections.\n\n" +
"If you can still see his offer in the offerbook you can try to take the offer again.");
}
@Override

View file

@ -392,8 +392,8 @@ public class DisputeSummaryPopup extends Popup {
disputeManager.sendDisputeResultMessage(disputeResult, dispute, text);
if (!finalPeersDispute.isClosed())
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () -> new Popup().information(
"You need to close also the trading peers ticket!").show());
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () ->
new Popup().instruction("You need to close also the trading peers ticket!").show());
hide();

View file

@ -143,7 +143,7 @@ public class EmptyWalletPopup extends Popup {
emptyWalletButton.setDisable(true);
log.debug("wallet empty successful");
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () -> new Popup()
.information("The balance of your wallet was successfully transferred.")
.feedback("The balance of your wallet was successfully transferred.")
.onClose(() -> blurAgain()).show());
},
(errorMessage) -> {

View file

@ -155,7 +155,7 @@ public class OfferDetailsPopup extends Popup {
if (takeOfferHandlerOptional.isPresent())
addLabelTextField(gridPane, rowIndex, "Offer type:", formatter.getDirectionForTaker(offer.getDirection()), Layout.FIRST_ROW_DISTANCE);
else
addLabelTextField(gridPane, rowIndex, "Offer type:", formatter.getOfferDirection(offer.getDirection()), Layout.FIRST_ROW_DISTANCE);
addLabelTextField(gridPane, rowIndex, "Offer type:", formatter.getOfferDirectionForOfferer(offer.getDirection()), Layout.FIRST_ROW_DISTANCE);
if (takeOfferHandlerOptional.isPresent()) {
addLabelTextField(gridPane, ++rowIndex, "Trade amount:", formatter.formatCoinWithCode(tradeAmount));

View file

@ -17,6 +17,7 @@
package io.bitsquare.gui.main.popups;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
@ -74,6 +75,8 @@ public class Popup {
private ChangeListener<Number> positionListener;
private Timer centerTime;
protected double buttonDistance = 20;
private String type;
private AwesomeIcon awesomeIcon;
///////////////////////////////////////////////////////////////////////////////////////////
@ -152,30 +155,75 @@ public class Popup {
}
public Popup notification(String message) {
// TODO use icons
this.headLine = "Notification";
type = "notification";
if (headLine == null)
this.headLine = "Notification";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup instruction(String message) {
type = "instruction";
if (headLine == null)
this.headLine = "Instruction";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup backgroundInfo(String message) {
type = "backgroundInfo";
if (headLine == null)
this.headLine = "Background information";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup feedback(String message) {
type = "feedback";
if (headLine == null)
this.headLine = "Feedback";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup confirmation(String message) {
type = "confirmation";
if (headLine == null)
this.headLine = "Confirmation";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup information(String message) {
this.headLine = "Information";
type = "information";
if (headLine == null)
this.headLine = "Information";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup warning(String message) {
this.headLine = "Warning";
type = "warning";
awesomeIcon = AwesomeIcon.LIGHTBULB;
if (headLine == null)
this.headLine = "Warning";
this.message = message;
setTruncatedMessage();
return this;
}
public Popup error(String message) {
type = "error";
showReportErrorButtons();
this.headLine = "Error";
if (headLine == null)
this.headLine = "Error";
this.message = message;
setTruncatedMessage();
return this;
@ -186,11 +234,6 @@ public class Popup {
return this;
}
public Popup hideReportErrorButtons() {
this.showReportErrorButtons = false;
return this;
}
public Popup message(String message) {
this.message = message;
setTruncatedMessage();
@ -218,7 +261,7 @@ public class Popup {
}
public Popup dontShowAgainId(String dontShowAgainId, Preferences preferences) {
this.dontShowAgainId = dontShowAgainId;
this.dontShowAgainId = type + "_" + dontShowAgainId;
this.preferences = preferences;
return this;
}
@ -323,10 +366,21 @@ public class Popup {
protected void addHeadLine() {
if (headLine != null) {
++rowIndex;
/* Label icon = AwesomeDude.createIconLabel(awesomeIcon, "40.0");
icon.getStyleClass().add("popup-icon-" + type);
GridPane.setHalignment(icon, HPos.RIGHT);
GridPane.setRowIndex(icon, ++rowIndex);
GridPane.setColumnIndex(icon, 1);
GridPane.setMargin(icon, new Insets(0, 0, -10, 0));
gridPane.getChildren().add(icon);*/
headLineLabel = new Label(BSResources.get(headLine));
headLineLabel.setMouseTransparent(true);
GridPane.setHalignment(headLineLabel, HPos.LEFT);
GridPane.setRowIndex(headLineLabel, ++rowIndex);
GridPane.setRowIndex(headLineLabel, rowIndex);
GridPane.setColumnSpan(headLineLabel, 2);
gridPane.getChildren().addAll(headLineLabel);
}
@ -406,7 +460,7 @@ public class Popup {
private void addDontShowAgainCheckBox() {
if (dontShowAgainId != null && preferences != null) {
CheckBox dontShowAgainCheckBox = addCheckBox(gridPane, rowIndex, "Don't show again", 30);
CheckBox dontShowAgainCheckBox = addCheckBox(gridPane, rowIndex, "Don't show again", buttonDistance - 1);
GridPane.setColumnIndex(dontShowAgainCheckBox, 0);
GridPane.setHalignment(dontShowAgainCheckBox, HPos.LEFT);
dontShowAgainCheckBox.setOnAction(e -> {

View file

@ -40,13 +40,13 @@ public class TacPopup extends Popup {
preferences.setTacAccepted(true);
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET)
UserThread.runAfter(() -> new Popup()
.headLine("Important information!")
.warning("This software is still in alpha version.\n" +
"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.01 BTC for the alpha version " +
"security deposit have been reduced to 0.1 BTC for the alpha version " +
"when using Mainnet.")
.headLine("Important information!")
.actionButtonText("I understand and want to use Mainnet")
.closeButtonText("Restart and use Testnet")
.onClose(() -> {

View file

@ -156,8 +156,8 @@ public class WalletPasswordPopup extends Popup {
hide();
} else {
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () -> new Popup()
.headLine("Wrong password")
.message("Please try entering your password again, carefully checking for typos or spelling errors.")
.warning("You entered the wrong password.\n\n" +
"Please try entering your password again, carefully checking for typos or spelling errors.")
.onClose(() -> blurAgain()).show());
}
});

View file

@ -91,8 +91,9 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
model.onCancelOpenOffer(openOffer,
() -> {
log.debug("Remove offer was successful");
new Popup().information("You can withdraw the funds you paid in from the funds screens.")
.onClose(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class))
new Popup().instruction("You can withdraw the funds you paid in from the \"Fund/Available for withdrawal\" screen.")
.actionButtonText("Go to \"Funds/Available for withdrawal\"")
.onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class))
.show();
},
(message) -> {

View file

@ -70,7 +70,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
private final User user;
private final KeyRing keyRing;
public final DisputeManager disputeManager;
private final Navigation navigation;
public final Navigation navigation;
private final WalletPasswordPopup walletPasswordPopup;
private final NotificationCenter notificationCenter;

View file

@ -32,7 +32,6 @@ import io.bitsquare.payment.BlockChainAccountContractData;
import io.bitsquare.payment.PaymentAccountContractData;
import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.Trade;
import io.bitsquare.user.PopupId;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
@ -70,26 +69,27 @@ public class BuyerStep2View extends TradeStepView {
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
String message = "";
if (paymentAccountContractData instanceof BlockChainAccountContractData)
message = "Please transfer from your external " +
message = "Your trade has reached at least one blockchain confirmation.\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 receivers address 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)
message = "Please go to your online banking web page and pay\n" +
message = "Your trade has reached at least one blockchain confirmation.\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\n" +
"Please don't forget to add the reference text " + trade.getShortId() +
" so the receiver can assign your payment to this trade.\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, Trade or Bitsquare.\n\n" +
"You can copy & paste the values from the main screen after closing that popup.";
"Bitcoin, Btc or Bitsquare.";
new Popup().headLine("Notification for trade with ID " + trade.getShortId())
.message(message)
.closeButtonText("I understand")
new Popup().headLine("Attention required for trade with ID " + trade.getShortId())
.instruction(message)
.dontShowAgainId(id, preferences)
.show();
}
@ -210,13 +210,15 @@ public class BuyerStep2View extends TradeStepView {
private void onPaymentStarted() {
if (model.p2PService.isBootstrapped()) {
String key = PopupId.PAYMENT_SENT;
String key = "confirmPaymentStarted";
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup().headLine("Confirmation")
.message("Did you transfer the payment to your trading partner?")
new 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")
.actionButtonText("Yes, I have started the payment")
.closeButtonText("No")
.onAction(this::confirmPaymentStarted)
.show();

View file

@ -19,15 +19,23 @@ package io.bitsquare.gui.main.portfolio.pendingtrades.steps.buyer;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.app.Log;
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.popups.Popup;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel;
import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeStepView;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.Layout;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import static io.bitsquare.gui.util.FormBuilder.*;
@ -108,29 +116,81 @@ public class BuyerStep5View extends TradeStepView {
withdrawButton = addButtonAfterGroup(gridPane, ++gridRow, "Withdraw to external wallet");
withdrawButton.setOnAction(e -> {
withdrawButton.setDisable(true);
model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(),
() -> {
String id = "TradeCompletedInfoPopup";
if (preferences.showAgain(id)) {
new Popup().information("You can review your completed trades under \"Portfolio/History\" or " +
"review your transactions under \"Funds/Transactions\"")
.dontShowAgainId(id, preferences)
.show();
}
withdrawButton.setDisable(true);
},
(errorMessage, throwable) -> {
withdrawButton.setDisable(false);
if (throwable == null)
new Popup().warning(errorMessage).show();
else
new Popup().error("An error occurred:\n" + throwable.getMessage()).show();
});
reviewWithdrawal();
});
String id = "tradeCompleteInfo";
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(id, preferences)
.show();
}
private void doWithdrawal() {
model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(),
() -> {
String id = "tradeCompleteWithdrawCompletedInfo";
if (preferences.showAgain(id)) {
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\"")
.actionButtonText("Go to \"Transactions\"")
.onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class))
.dontShowAgainId(id, preferences)
.show();
}
withdrawButton.setDisable(true);
},
(errorMessage, throwable) -> {
withdrawButton.setDisable(false);
if (throwable != null && throwable.getMessage() != null)
new Popup().error(errorMessage + "\n\n" + throwable.getMessage()).show();
else
new Popup().error(errorMessage).show();
});
}
private void reviewWithdrawal() {
Coin senderAmount = trade.getPayoutAmount();
WalletService walletService = model.dataModel.walletService;
String fromAddresses = walletService.getAddressEntryByOfferId(trade.getId()).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 id = "reviewWithdrawalAtTradeComplete";
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(id, preferences)
.show();
}
} 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();
}
}
protected String getBtcTradeAmountLabel() {

View file

@ -30,7 +30,6 @@ import io.bitsquare.payment.BlockChainAccountContractData;
import io.bitsquare.payment.PaymentAccountContractData;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade;
import io.bitsquare.user.PopupId;
import io.bitsquare.user.Preferences;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@ -71,26 +70,25 @@ public class SellerStep3View extends TradeStepView {
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
String message;
String tradeAmountWithCode = model.formatter.formatFiatWithCode(trade.getTradeVolume());
String currencyName = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode());
if (paymentAccountContractData instanceof BlockChainAccountContractData) {
String nameByCode = CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode());
String address = ((BlockChainAccountContractData) paymentAccountContractData).getAddress();
message = "Please check on your favorite " +
nameByCode +
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" +
"has already sufficient blockchain confirmations.\n" +
"The payment amount has to be " + tradeAmountWithCode + "\n\n" +
"You can copy & paste your " + nameByCode + " address from the main screen after " +
"You can copy & paste your " + currencyName + " address from the main screen after " +
"closing that popup.";
} else {
message = "Please go to your online banking web page and check if you have received " +
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();
"The reference text of the transaction is: \"" + trade.getShortId() + "\"";
}
new Popup().headLine("Notification for trade with ID " + trade.getShortId())
.message(message)
.closeButtonText("I understand")
new Popup().headLine("Attention required for trade with ID " + trade.getShortId())
.instruction(message)
.dontShowAgainId(id, preferences)
.show();
@ -220,16 +218,17 @@ public class SellerStep3View extends TradeStepView {
log.debug("onPaymentReceived");
if (model.p2PService.isBootstrapped()) {
Preferences preferences = model.dataModel.preferences;
String key = PopupId.PAYMENT_RECEIVED;
String key = "confirmPaymentReceived";
if (preferences.showAgain(key) && !BitsquareApp.DEV_MODE) {
new Popup().headLine("Confirmation")
.message("Did you receive the payment from your trading partner?\n\n" +
"Please note that as soon you have confirmed the locked bitcoin will be released.\n" +
"There is no way to reverse a bitcoin payment.")
new Popup()
.headLine("Confirm that you have received the payment")
.confirmation("Have you received the " + model.dataModel.getCurrencyCode() + " payment from your trading partner?\n\n" +
"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("No")
.actionButtonText("Yes, I have received the payment")
.closeButtonText("Cancel")
.onAction(this::confirmPaymentReceived)
.show();
} else {

View file

@ -57,8 +57,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
private ComboBox<String> userLanguageComboBox;
private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox;
private CheckBox useAnimationsCheckBox, useEffectsCheckBox, showNotificationsCheckBox, showInstructionsCheckBox,
autoSelectArbitratorsCheckBox;
private CheckBox useAnimationsCheckBox, useEffectsCheckBox, autoSelectArbitratorsCheckBox;
private int gridRow = 0;
//private InputTextField transactionFeeInputTextField;
private ChangeListener<Boolean> transactionFeeFocusedListener;
@ -78,6 +77,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
public final ObservableList<CryptoCurrency> cryptoCurrencies;
public final ObservableList<CryptoCurrency> allCryptoCurrencies;
public final ObservableList<TradeCurrency> tradeCurrencies;
private Button resetDontShowAgainButton;
///////////////////////////////////////////////////////////////////////////////////////////
@ -293,16 +293,12 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 4, "Display options", Layout.GROUP_DISTANCE);
GridPane.setColumnSpan(titledGroupBg, 4);
useAnimationsCheckBox = addLabelCheckBox(root, gridRow, "Use animations:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
Tuple2<Label, CheckBox> labelCheckBoxTuple2 = addLabelCheckBox(root, gridRow, "Show notifications:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE);
showNotificationsCheckBox = labelCheckBoxTuple2.second;
GridPane.setColumnIndex(labelCheckBoxTuple2.first, 2);
GridPane.setColumnIndex(showNotificationsCheckBox, 3);
Tuple2<Label, Button> labelButton = addLabelButton(root, gridRow, "Reset all don't show again flags:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE);
resetDontShowAgainButton = labelButton.second;
GridPane.setColumnIndex(labelButton.first, 2);
GridPane.setColumnIndex(resetDontShowAgainButton, 3);
useEffectsCheckBox = addLabelCheckBox(root, ++gridRow, "Use effects:", "").second;
labelCheckBoxTuple2 = addLabelCheckBox(root, gridRow, "Show instruction popups:", "");
showInstructionsCheckBox = labelCheckBoxTuple2.second;
GridPane.setColumnIndex(labelCheckBoxTuple2.first, 2);
GridPane.setColumnIndex(showInstructionsCheckBox, 3);
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -406,11 +402,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
useEffectsCheckBox.setSelected(preferences.getUseEffects());
useEffectsCheckBox.setOnAction(e -> preferences.setUseEffects(useEffectsCheckBox.isSelected()));
showNotificationsCheckBox.setSelected(preferences.getShowNotifications());
showNotificationsCheckBox.setOnAction(e -> preferences.setShowNotifications(showNotificationsCheckBox.isSelected()));
showInstructionsCheckBox.setSelected(preferences.getShowInstructions());
showInstructionsCheckBox.setOnAction(e -> preferences.setShowInstructions(showInstructionsCheckBox.isSelected()));
resetDontShowAgainButton.setOnAction(e -> preferences.resetDontShowAgainForType());
autoSelectArbitratorsCheckBox.setSelected(preferences.getAutoSelectArbitrators());
autoSelectArbitratorsCheckBox.setOnAction(e -> preferences.setAutoSelectArbitrators(autoSelectArbitratorsCheckBox.isSelected()));
@ -428,8 +420,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
//btcDenominationComboBox.setOnAction(null);
userLanguageComboBox.setOnAction(null);
blockChainExplorerComboBox.setOnAction(null);
showNotificationsCheckBox.setOnAction(null);
showInstructionsCheckBox.setOnAction(null);
// transactionFeeInputTextField.textProperty().unbind();
/// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);
}

View file

@ -412,13 +412,13 @@ public class BSFormatter {
}
public String getDirectionForTaker(Offer.Direction direction) {
return direction == Offer.Direction.BUY ? "You are selling bitcoin (take an offer from someone who wants to buy bitcoin)" :
"You are buying bitcoin (take an offer from someone who wants to sell bitcoin)";
return direction == Offer.Direction.BUY ? "You are selling bitcoin (by taking an offer from someone who wants to buy bitcoin)" :
"You are buying bitcoin (by taking an offer from someone who wants to sell bitcoin)";
}
public String getOfferDirection(Offer.Direction direction) {
return direction == Offer.Direction.BUY ? "Offer for buying bitcoin" :
"Offer for selling bitcoin";
public String getOfferDirectionForOfferer(Offer.Direction direction) {
return direction == Offer.Direction.BUY ? "You are creating an offer for buying bitcoin" :
"You are creating an offer for selling bitcoin";
}
public String getRole(boolean isBuyerOffererAndSellerTaker, boolean isOfferer) {

View file

@ -75,7 +75,7 @@ createOffer.advancedBox.currency=Currency:
createOffer.advancedBox.county=Payments account country:
createOffer.advancedBox.info=Your trading partners must fulfill your offer restrictions. You can edit the accepted countries, languages and arbitrators in the settings. The payments account details are used from your current selected payments account (if you have multiple payments accounts).
createOffer.success.headline=Your offer has been published.
createOffer.success.headline=Your offer has been published
createOffer.success.info=You can manage your open offers in the \"Portfolio\" screen under \"My open offers\".
createOffer.error.message=An error occurred when placing the offer.\n\n{0}
@ -135,9 +135,8 @@ takeOffer.advancedBox.county=Payments account country:
takeOffer.advancedBox.info=These are the offer restrictions your trading partner has defined in his offer. Your \
settings are matching those constraints and you are able to trade with him.
takeOffer.success.headline=Your have successfully published the deposit.
takeOffer.success.info=The trade has started now.\n You can see the status of your trade in the \"Portfolio\" screen \
under \"Open trades\".
takeOffer.success.headline=You have successfully taken an offer
takeOffer.success.info=You can see the status of your trade at the \"Portfolio\" screen under \"Open trades\".
takeOffer.error.message=An error occurred when taking the offer.\n\n{0}
# Payment methods