diff --git a/core/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java b/core/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java index 5e3f639cd1..0154dc073b 100644 --- a/core/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java +++ b/core/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java @@ -17,26 +17,21 @@ package io.bitsquare.btc; -import com.google.common.annotations.VisibleForTesting; -import org.bitcoinj.core.*; -import org.bitcoinj.wallet.CoinSelection; -import org.bitcoinj.wallet.CoinSelector; +import org.bitcoinj.core.Address; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.TransactionOutput; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; -import java.math.BigInteger; -import java.util.*; - -import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Set; /** * We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation. * We lookup for spendable outputs which matches our address of our addressEntry. */ -class AddressBasedCoinSelector implements CoinSelector { +class AddressBasedCoinSelector extends SavingsWalletCoinSelector { private static final Logger log = LoggerFactory.getLogger(AddressBasedCoinSelector.class); - private final NetworkParameters params; @Nullable private Set addressEntries; @Nullable @@ -47,62 +42,22 @@ class AddressBasedCoinSelector implements CoinSelector { /////////////////////////////////////////////////////////////////////////////////////////// public AddressBasedCoinSelector(NetworkParameters params) { - this.params = params; + super(params); } public AddressBasedCoinSelector(NetworkParameters params, @Nullable AddressEntry addressEntry) { - this.params = params; + super(params); this.addressEntry = addressEntry; } - public AddressBasedCoinSelector(NetworkParameters params, Set addressEntries) { - this.params = params; + public AddressBasedCoinSelector(NetworkParameters params, @Nullable Set addressEntries) { + super(params); this.addressEntries = addressEntries; } - @VisibleForTesting - private static void sortOutputs(ArrayList outputs) { - Collections.sort(outputs, (a, b) -> { - int depth1 = a.getParentTransactionDepthInBlocks(); - int depth2 = b.getParentTransactionDepthInBlocks(); - Coin aValue = a.getValue(); - Coin bValue = b.getValue(); - BigInteger aCoinDepth = BigInteger.valueOf(aValue.value).multiply(BigInteger.valueOf(depth1)); - BigInteger bCoinDepth = BigInteger.valueOf(bValue.value).multiply(BigInteger.valueOf(depth2)); - int c1 = bCoinDepth.compareTo(aCoinDepth); - if (c1 != 0) return c1; - // The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size. - int c2 = bValue.compareTo(aValue); - if (c2 != 0) return c2; - // They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering. - checkNotNull(a.getParentTransactionHash(), "a.getParentTransactionHash() must not be null"); - checkNotNull(b.getParentTransactionHash(), "b.getParentTransactionHash() must not be null"); - BigInteger aHash = a.getParentTransactionHash().toBigInteger(); - BigInteger bHash = b.getParentTransactionHash().toBigInteger(); - return aHash.compareTo(bHash); - }); - } - - private static boolean isInBlockChainOrPending(Transaction tx) { - // Pick chain-included transactions and transactions that are pending. - TransactionConfidence confidence = tx.getConfidence(); - TransactionConfidence.ConfidenceType type = confidence.getConfidenceType(); - - log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers()); - return type.equals(TransactionConfidence.ConfidenceType.BUILDING) || - type.equals(TransactionConfidence.ConfidenceType.PENDING); - } - - /** - * Sub-classes can override this to just customize whether transactions are usable, but keep age sorting. - */ - private boolean shouldSelect(Transaction tx) { - return isInBlockChainOrPending(tx); - } - - private boolean matchesRequiredAddress(TransactionOutput transactionOutput) { - if (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash - ()) { + @Override + protected boolean matchesRequirement(TransactionOutput transactionOutput) { + if (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash()) { Address addressOutput = transactionOutput.getScriptPubKey().getToAddress(params); log.trace("matchesRequiredAddress(es)?"); log.trace(addressOutput.toString()); @@ -123,48 +78,9 @@ class AddressBasedCoinSelector implements CoinSelector { log.trace("No match found at matchesRequiredAddress addressOutput / addressEntries " + addressOutput.toString () + " / " + addressEntries.toString()); - } else { - // use savings wallet - return true; } } return false; } - @Override - public CoinSelection select(Coin target, List candidates) { - log.trace("candidates.size: " + candidates.size()); - long targetAsLong = target.longValue(); - log.trace("value needed: " + targetAsLong); - HashSet selected = new HashSet<>(); - // Sort the inputs by age*value so we get the highest "coindays" spent. - ArrayList sortedOutputs = new ArrayList<>(candidates); - // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting - // them in order to improve performance. - if (!target.equals(NetworkParameters.MAX_MONEY)) { - sortOutputs(sortedOutputs); - } - // Now iterate over the sorted outputs until we have got as close to the target as possible or a little - // bit over (excessive value will be change). - long total = 0; - for (TransactionOutput output : sortedOutputs) { - if (total >= targetAsLong) { - break; - } - // Only pick chain-included transactions, or transactions that are ours and pending. - // Only select outputs from our defined address(es) - if (!shouldSelect(output.getParentTransaction()) || !matchesRequiredAddress(output)) { - continue; - } - - selected.add(output); - total += output.getValue().longValue(); - - log.debug("adding up outputs: output/total: " + output.getValue().longValue() + "/" + total); - } - // Total may be lower than target here, if the given candidates were insufficient to create to requested - // transaction. - return new CoinSelection(Coin.valueOf(total), selected); - } - } diff --git a/core/src/main/java/io/bitsquare/btc/SavingsWalletCoinSelector.java b/core/src/main/java/io/bitsquare/btc/SavingsWalletCoinSelector.java new file mode 100644 index 0000000000..179f136ed2 --- /dev/null +++ b/core/src/main/java/io/bitsquare/btc/SavingsWalletCoinSelector.java @@ -0,0 +1,131 @@ +/* + * 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 . + */ + +package io.bitsquare.btc; + +import com.google.common.annotations.VisibleForTesting; +import org.bitcoinj.core.*; +import org.bitcoinj.wallet.CoinSelection; +import org.bitcoinj.wallet.CoinSelector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation. + * We lookup for spendable outputs which matches our address of our addressEntry. + */ +class SavingsWalletCoinSelector implements CoinSelector { + private static final Logger log = LoggerFactory.getLogger(SavingsWalletCoinSelector.class); + protected final NetworkParameters params; + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + public SavingsWalletCoinSelector(NetworkParameters params) { + this.params = params; + } + + @VisibleForTesting + private static void sortOutputs(ArrayList outputs) { + Collections.sort(outputs, (a, b) -> { + int depth1 = a.getParentTransactionDepthInBlocks(); + int depth2 = b.getParentTransactionDepthInBlocks(); + Coin aValue = a.getValue(); + Coin bValue = b.getValue(); + BigInteger aCoinDepth = BigInteger.valueOf(aValue.value).multiply(BigInteger.valueOf(depth1)); + BigInteger bCoinDepth = BigInteger.valueOf(bValue.value).multiply(BigInteger.valueOf(depth2)); + int c1 = bCoinDepth.compareTo(aCoinDepth); + if (c1 != 0) return c1; + // The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size. + int c2 = bValue.compareTo(aValue); + if (c2 != 0) return c2; + // They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering. + checkNotNull(a.getParentTransactionHash(), "a.getParentTransactionHash() must not be null"); + checkNotNull(b.getParentTransactionHash(), "b.getParentTransactionHash() must not be null"); + BigInteger aHash = a.getParentTransactionHash().toBigInteger(); + BigInteger bHash = b.getParentTransactionHash().toBigInteger(); + return aHash.compareTo(bHash); + }); + } + + private static boolean isInBlockChainOrPending(Transaction tx) { + // Pick chain-included transactions and transactions that are pending. + TransactionConfidence confidence = tx.getConfidence(); + TransactionConfidence.ConfidenceType type = confidence.getConfidenceType(); + + log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers()); + return type.equals(TransactionConfidence.ConfidenceType.BUILDING) || + type.equals(TransactionConfidence.ConfidenceType.PENDING); + } + + /** + * Sub-classes can override this to just customize whether transactions are usable, but keep age sorting. + */ + protected boolean shouldSelect(Transaction tx) { + return isInBlockChainOrPending(tx); + } + + protected boolean matchesRequirement(TransactionOutput transactionOutput) { + return (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash()); + } + + @Override + public CoinSelection select(Coin target, List candidates) { + log.trace("candidates.size: " + candidates.size()); + long targetAsLong = target.longValue(); + log.trace("value needed: " + targetAsLong); + HashSet selected = new HashSet<>(); + // Sort the inputs by age*value so we get the highest "coindays" spent. + ArrayList sortedOutputs = new ArrayList<>(candidates); + // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting + // them in order to improve performance. + if (!target.equals(NetworkParameters.MAX_MONEY)) { + sortOutputs(sortedOutputs); + } + // Now iterate over the sorted outputs until we have got as close to the target as possible or a little + // bit over (excessive value will be change). + long total = 0; + for (TransactionOutput output : sortedOutputs) { + if (total >= targetAsLong) { + break; + } + // Only pick chain-included transactions, or transactions that are ours and pending. + // Only select outputs from our defined address(es) + if (!shouldSelect(output.getParentTransaction()) || !matchesRequirement(output)) { + continue; + } + + selected.add(output); + total += output.getValue().longValue(); + + log.debug("adding up outputs: output/total: " + output.getValue().longValue() + "/" + total); + } + // Total may be lower than target here, if the given candidates were insufficient to create to requested + // transaction. + return new CoinSelection(Coin.valueOf(total), selected); + } + +} diff --git a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java index 5089dd1720..932fb8cf20 100644 --- a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java +++ b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java @@ -158,7 +158,7 @@ public class TradeWalletService { sendRequest.shuffleOutputs = false; sendRequest.aesKey = aesKey; if (useSavingsWallet) - sendRequest.coinSelector = new AddressBasedCoinSelector(params); + sendRequest.coinSelector = new SavingsWalletCoinSelector(params); else sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry); // We use a fixed fee diff --git a/gui/src/main/java/io/bitsquare/gui/components/BalanceTextField.java b/gui/src/main/java/io/bitsquare/gui/components/BalanceTextField.java index 11d1eaa4f0..ee4e6f51f2 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/BalanceTextField.java +++ b/gui/src/main/java/io/bitsquare/gui/components/BalanceTextField.java @@ -18,7 +18,6 @@ package io.bitsquare.gui.components; import io.bitsquare.btc.WalletService; -import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.gui.util.BSFormatter; import javafx.scene.control.TextField; import javafx.scene.effect.BlurType; @@ -26,14 +25,11 @@ import javafx.scene.effect.DropShadow; import javafx.scene.effect.Effect; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; -import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; -import org.bitcoinj.core.Transaction; public class BalanceTextField extends AnchorPane { private static WalletService walletService; - private BalanceListener balanceListener; private Coin targetAmount; public static void setWalletService(WalletService walletService) { @@ -65,22 +61,6 @@ public class BalanceTextField extends AnchorPane { this.formatter = formatter; } - public void setupBalanceListener(Address address) { - balanceListener = new BalanceListener(address) { - @Override - public void onBalanceChanged(Coin balance, Transaction tx) { - updateBalance(balance); - } - }; - walletService.addBalanceListener(balanceListener); - updateBalance(walletService.getBalanceForAddress(address)); - } - - public void cleanup() { - if (balanceListener != null) - walletService.removeBalanceListener(balanceListener); - } - public void setBalance(Coin balance) { updateBalance(balance); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java b/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java index 80a2527f60..613ecf05ed 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/deposit/DepositView.java @@ -37,6 +37,7 @@ import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.Layout; import io.bitsquare.user.Preferences; import javafx.beans.property.ReadOnlyObjectWrapper; +import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.collections.transformation.SortedList; @@ -91,6 +92,7 @@ public class DepositView extends ActivatableView { private InputTextField amountTextField; private Subscription amountTextFieldSubscription; private String paymentLabel; + private ChangeListener tableViewSelectionListener; /////////////////////////////////////////////////////////////////////////////////////////// @@ -108,8 +110,15 @@ public class DepositView extends ActivatableView { @Override public void initialize() { + // trigger creation of at least 1 savings address + walletService.getUnusedSavingsAddressEntry(); + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setPlaceholder(new Label("No deposit addresses are generated yet")); + tableViewSelectionListener = (observableValue, oldValue, newValue) -> { + if (newValue != null) + fillForm(newValue.getAddressString()); + }; setSelectColumnCellFactory(); setAddressColumnCellFactory(); @@ -174,12 +183,17 @@ public class DepositView extends ActivatableView { generateNewAddressButton.setOnAction(event -> { boolean hasUnUsedAddress = observableList.stream().filter(e -> e.getNumTxOutputs() == 0).findAny().isPresent(); if (hasUnUsedAddress) { - new Popup().warning("You have addresses which are not used in any transaction.\n" + - "Please select in the address table any unused address.").show(); + new Popup().warning("You have already at least one address which is not used yet in any transaction.\n" + + "Please select in the address table an unused address.").show(); } else { AddressEntry newSavingsAddressEntry = walletService.getNewSavingsAddressEntry(); - fillForm(newSavingsAddressEntry.getAddressString()); + //fillForm(newSavingsAddressEntry.getAddressString()); updateList(); + observableList.stream() + .filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString())) + .findAny() + .ifPresent(depositListItem -> tableView.getSelectionModel().select(depositListItem)); + } }); @@ -212,6 +226,7 @@ public class DepositView extends ActivatableView { @Override protected void activate() { + tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener); sortedList.comparatorProperty().bind(tableView.comparatorProperty()); tableView.setItems(sortedList); updateList(); @@ -225,6 +240,7 @@ public class DepositView extends ActivatableView { @Override protected void deactivate() { + tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener); sortedList.comparatorProperty().unbind(); observableList.forEach(DepositListItem::cleanup); walletService.removeBalanceListener(balanceListener); @@ -256,7 +272,6 @@ public class DepositView extends ActivatableView { addressTextField.setAddress(address); updateQRCode(); - } private void updateQRCode() { @@ -343,7 +358,7 @@ public class DepositView extends ActivatableView { if (item != null && !empty) { if (button == null) { button = new Button("Select"); - button.setOnAction(e -> fillForm(item.getAddressString())); + button.setOnAction(e -> tableView.getSelectionModel().select(item)); setGraphic(button); } } else { @@ -379,7 +394,10 @@ public class DepositView extends ActivatableView { if (item != null && !empty) { String addressString = item.getAddressString(); field = new HyperlinkWithIcon(addressString, AwesomeIcon.EXTERNAL_LINK); - field.setOnAction(event -> openBlockExplorer(item)); + field.setOnAction(event -> { + openBlockExplorer(item); + tableView.getSelectionModel().select(item); + }); field.setTooltip(new Tooltip("Open external blockchain explorer for " + "address: " + addressString)); setGraphic(field); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java index f38ec1dd5f..934744c47b 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java @@ -178,11 +178,11 @@ class CreateOfferDataModel extends ActivatableDataModel { addListeners(); paymentAccounts.setAll(user.getPaymentAccounts()); - calculateTotalToPay(); - updateBalance(); if (isTabSelected) priceFeed.setCurrencyCode(tradeCurrencyCode.get()); + + updateBalance(); } @Override @@ -225,6 +225,10 @@ class CreateOfferDataModel extends ActivatableDataModel { paymentAccount = account; priceFeed.setCurrencyCode(tradeCurrencyCode.get()); + + calculateVolume(); + calculateTotalToPay(); + updateBalance(); } void onTabSelected(boolean isSelected) { @@ -373,6 +377,8 @@ class CreateOfferDataModel extends ActivatableDataModel { !priceAsFiat.get().isZero()) { volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get())); } + + updateBalance(); } void calculateAmount() { @@ -402,7 +408,7 @@ class CreateOfferDataModel extends ActivatableDataModel { Coin savingWalletBalance = walletService.getSavingWalletBalance(); totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance); - if (totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0) + if (totalToPayAsCoin.get() != null && totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0) balance.set(totalToPayAsCoin.get()); else balance.set(totalAvailableBalance); @@ -410,22 +416,21 @@ class CreateOfferDataModel extends ActivatableDataModel { balance.set(tradeWalletBalance); } - missingCoin.set(totalToPayAsCoin.get().subtract(balance.get())); - if (missingCoin.get().isNegative()) - missingCoin.set(Coin.ZERO); + if (totalToPayAsCoin.get() != null) { + missingCoin.set(totalToPayAsCoin.get().subtract(balance.get())); + if (missingCoin.get().isNegative()) + missingCoin.set(Coin.ZERO); + } isWalletFunded.set(isBalanceSufficient(balance.get())); - if (isWalletFunded.get()) { - //walletService.removeBalanceListener(balanceListener); - if (walletFundedNotification == null) { - walletFundedNotification = new Notification() - .headLine("Trading wallet update") - .notification("Your trading wallet is sufficiently funded.\n" + - "Amount: " + formatter.formatCoinWithCode(totalToPayAsCoin.get())) - .autoClose(); + if (totalToPayAsCoin.get() != null && isWalletFunded.get() && 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(); - } + walletFundedNotification.show(); } } diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java index ca4ba75985..24cb88048f 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java @@ -62,7 +62,6 @@ import javafx.stage.Window; import javafx.util.StringConverter; import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; -import org.bitcoinj.core.Coin; import org.bitcoinj.uri.BitcoinURI; import org.controlsfx.control.PopOver; import org.fxmisc.easybind.EasyBind; @@ -120,10 +119,10 @@ public class CreateOfferView extends ActivatableViewAndModel tradeCurrencyCodeListener; private ImageView qrCodeImageView; - private ChangeListener balanceListener; private HBox fundingHBox; private Subscription isSpinnerVisibleSubscription; private Subscription cancelButton2StyleSubscription; + private Subscription balanceSubscription; /////////////////////////////////////////////////////////////////////////////////////////// @@ -150,7 +149,6 @@ public class CreateOfferView extends ActivatableViewAndModel balanceTextField.setBalance(newValue); paymentAccountsComboBox.setConverter(new StringConverter() { @Override @@ -185,7 +183,6 @@ public class CreateOfferView extends ActivatableViewAndModel cancelButton2.setId(isVisible ? "cancel-button" : null)); + + balanceSubscription = EasyBind.subscribe(model.dataModel.balance, newValue -> balanceTextField.setBalance(newValue)); } private void removeSubscriptions() { isSpinnerVisibleSubscription.unsubscribe(); cancelButton2StyleSubscription.unsubscribe(); + balanceSubscription.unsubscribe(); } private void createListeners() { @@ -600,7 +597,6 @@ public class CreateOfferView extends ActivatableViewAndModel { + .closeButtonText("No") + .actionButtonText("Yes, cancel") + .onAction(() -> { close(); model.dataModel.swapTradeToSavings(); }) diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java index fe1f66af02..843c05fe56 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java @@ -131,7 +131,6 @@ class TakeOfferDataModel extends ActivatableDataModel { addBindings(); addListeners(); - calculateTotalToPay(); updateBalance(); // TODO In case that we have funded but restarted, or canceled but took again the offer we would need to @@ -353,9 +352,7 @@ class TakeOfferDataModel extends ActivatableDataModel { } isWalletFunded.set(isBalanceSufficient(balance.get())); - if (isWalletFunded.get()) { - // walletService.removeBalanceListener(balanceListener); - if (totalToPayAsCoin.get() != null && walletFundedNotification == null) { + if (totalToPayAsCoin.get() != null && isWalletFunded.get() && walletFundedNotification == null) { walletFundedNotification = new Notification() .headLine("Trading wallet update") .notification("Your trading wallet is sufficiently funded.\n" + @@ -364,7 +361,6 @@ class TakeOfferDataModel extends ActivatableDataModel { walletFundedNotification.show(); } - } } private boolean isBalanceSufficient(Coin balance) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java index a9ccf8450e..69b5017ba9 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java @@ -116,7 +116,6 @@ public class TakeOfferView extends ActivatableViewAndModel noSufficientFeeBinding; - private Subscription totalToPaySubscription; private Subscription cancelButton2StyleSubscription; @@ -143,6 +142,8 @@ public class TakeOfferView extends ActivatableViewAndModel { model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText()); amountTextField.setText(model.amount.get()); @@ -170,6 +171,8 @@ public class TakeOfferView extends ActivatableViewAndModel 1; paymentAccountsLabel.setVisible(showComboBox); @@ -232,7 +230,7 @@ public class TakeOfferView extends ActivatableViewAndModel balanceTextField.setBalance(newValue)); - totalToPaySubscription = EasyBind.subscribe(model.dataModel.totalToPayAsCoin, newValue -> balanceTextField.setTargetAmount(newValue)); cancelButton2StyleSubscription = EasyBind.subscribe(takeOfferButton.visibleProperty(), isVisible -> cancelButton2.setId(isVisible ? "cancel-button" : null)); } @@ -544,7 +544,6 @@ public class TakeOfferView extends ActivatableViewAndModel { - onTakeOffer(); - balanceTextField.cleanup(); - }); + takeOfferButton.setOnAction(e -> onTakeOffer()); cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel")); 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") - .onClose(() -> { + .closeButtonText("No") + .actionButtonText("Yes, cancel") + .onAction(() -> { model.dataModel.swapTradeToSavings(); close(); }) - .closeButtonText("Yes, cancel") .show(); } else { close(); @@ -785,7 +781,8 @@ public class TakeOfferView extends ActivatableViewAndModel im final BSFormatter formatter; private String amountRange; - private String addressAsString; private String paymentLabel; private boolean takeOfferRequested; private Trade trade; @@ -86,9 +84,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel im final ObjectProperty amountValidationResult = new SimpleObjectProperty<>(); - // Those are needed for the addressTextField - final ObjectProperty
address = new SimpleObjectProperty<>(); - private ChangeListener amountListener; private ChangeListener amountAsCoinListener; private ChangeListener isWalletFundedListener; @@ -169,9 +164,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel im checkNotNull(dataModel.getAddressEntry(), "dataModel.getAddressEntry() must not be null"); - addressAsString = dataModel.getAddressEntry().getAddressString(); - address.set(dataModel.getAddressEntry().getAddress()); - offerErrorListener = (observable, oldValue, newValue) -> { if (newValue != null) errorMessage.set(newValue); @@ -560,10 +552,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel im return paymentLabel; } - public String getAddressAsString() { - return addressAsString; - } - public String getPrice() { return price; }