From 6b3df246a125486edfa3e0fa801e53bba262a8b8 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Wed, 3 Feb 2016 18:06:34 +0100 Subject: [PATCH] Improve locked funds screen, add total balance for locked funds --- .../main/java/io/bitsquare/trade/Trade.java | 12 +- .../trade/protocol/trade/ProcessModel.java | 19 +- .../tasks/offerer/CreateAndSignContract.java | 4 +- .../offerer/ProcessPayDepositRequest.java | 2 +- .../tasks/taker/BroadcastTakeOfferFeeTx.java | 2 +- .../tasks/taker/CreateTakeOfferFeeTx.java | 6 +- .../tasks/taker/SendPayDepositRequest.java | 85 ++++---- .../tasks/taker/VerifyAndSignContract.java | 88 ++++---- .../gui/components/HyperlinkWithIcon.java | 2 +- .../java/io/bitsquare/gui/main/MainView.java | 6 +- .../io/bitsquare/gui/main/MainViewModel.java | 49 +++-- .../bitsquare/gui/main/funds/FundsView.fxml | 4 +- .../main/funds/reserved/ReservedListItem.java | 104 ++-------- .../gui/main/funds/reserved/ReservedView.fxml | 11 +- .../gui/main/funds/reserved/ReservedView.java | 188 +++++++++++------- .../transactions/TransactionsListItem.java | 2 +- .../funds/transactions/TransactionsView.fxml | 2 +- .../funds/transactions/TransactionsView.java | 14 +- .../main/funds/withdrawal/WithdrawalView.fxml | 2 +- .../main/funds/withdrawal/WithdrawalView.java | 116 +++++------ .../gui/popups/TradeDetailsPopup.java | 9 +- 21 files changed, 361 insertions(+), 366 deletions(-) diff --git a/core/src/main/java/io/bitsquare/trade/Trade.java b/core/src/main/java/io/bitsquare/trade/Trade.java index 99635d4ba4..c344f75007 100644 --- a/core/src/main/java/io/bitsquare/trade/Trade.java +++ b/core/src/main/java/io/bitsquare/trade/Trade.java @@ -171,7 +171,7 @@ abstract public class Trade implements Tradable, Model, Serializable { transient private ObjectProperty tradeAmountProperty; transient private ObjectProperty tradeVolumeProperty; @Nullable - private Transaction takeOfferFeeTx; + private String takeOfferFeeTxId; /////////////////////////////////////////////////////////////////////////////////////////// @@ -562,12 +562,12 @@ abstract public class Trade implements Tradable, Model, Serializable { return contractHash; } - public void setTakeOfferFeeTx(Transaction takeOfferFeeTx) { - this.takeOfferFeeTx = takeOfferFeeTx; + public void setTakeOfferFeeTxId(String takeOfferFeeTxId) { + this.takeOfferFeeTxId = takeOfferFeeTxId; } - public Transaction getTakeOfferFeeTx() { - return takeOfferFeeTx; + public String getTakeOfferFeeTxId() { + return takeOfferFeeTxId; } /////////////////////////////////////////////////////////////////////////////////////////// @@ -628,7 +628,7 @@ abstract public class Trade implements Tradable, Model, Serializable { "\n\tdisputeState=" + disputeState + "\n\ttradePeriodState=" + tradePeriodState + "\n\tdepositTx=" + depositTx + - "\n\ttakeOfferFeeTx.getHashAsString()=" + (takeOfferFeeTx != null ? takeOfferFeeTx.getHashAsString() : "") + + "\n\ttakeOfferFeeTxId=" + takeOfferFeeTxId + "\n\tcontract=" + contract + "\n\ttakerContractSignature.hashCode()='" + (takerContractSignature != null ? takerContractSignature.hashCode() : "") + '\'' + "\n\toffererContractSignature.hashCode()='" + (offererContractSignature != null ? offererContractSignature.hashCode() : "") + '\'' + diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java index e6fe12d32d..ed42d34ac1 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java @@ -36,6 +36,7 @@ import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.OpenOfferManager; import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.user.User; +import org.bitcoinj.core.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +66,6 @@ public class ProcessModel implements Model, Serializable { // Mutable public final TradingPeer tradingPeer; transient private TradeMessage tradeMessage; - private String takeOfferFeeTxId; private byte[] payoutTxSignature; private List takerAcceptedArbitratorNodeAddresses; @@ -78,6 +78,7 @@ public class ProcessModel implements Model, Serializable { private long changeOutputValue; @Nullable private String changeOutputAddress; + private Transaction takeOfferFeeTx; public ProcessModel() { tradingPeer = new TradingPeer(); @@ -194,14 +195,6 @@ public class ProcessModel implements Model, Serializable { this.payoutTxSignature = payoutTxSignature; } - public String getTakeOfferFeeTxId() { - return takeOfferFeeTxId; - } - - public void setTakeOfferFeeTxId(String takeOfferFeeTxId) { - this.takeOfferFeeTxId = takeOfferFeeTxId; - } - @Override public void persist() { } @@ -274,4 +267,12 @@ public class ProcessModel implements Model, Serializable { public String getChangeOutputAddress() { return changeOutputAddress; } + + public void setTakeOfferFeeTx(Transaction takeOfferFeeTx) { + this.takeOfferFeeTx = takeOfferFeeTx; + } + + public Transaction getTakeOfferFeeTx() { + return takeOfferFeeTx; + } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java index 172156029c..a07b7e6cb7 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/CreateAndSignContract.java @@ -43,7 +43,7 @@ public class CreateAndSignContract extends TradeTask { protected void run() { try { runInterceptHook(); - checkNotNull(processModel.getTakeOfferFeeTxId(), "processModel.getTakeOfferFeeTxId() must not be null"); + checkNotNull(trade.getTakeOfferFeeTxId(), "trade.getTakeOfferFeeTxId() must not be null"); TradingPeer taker = processModel.tradingPeer; PaymentAccountContractData offererPaymentAccountContractData = processModel.getPaymentAccountContractData(trade); @@ -58,7 +58,7 @@ public class CreateAndSignContract extends TradeTask { Contract contract = new Contract( processModel.getOffer(), trade.getTradeAmount(), - processModel.getTakeOfferFeeTxId(), + trade.getTakeOfferFeeTxId(), buyerNodeAddress, sellerNodeAddress, trade.getArbitratorNodeAddress(), diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java index 2d76fe5e00..25111cdaa8 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessPayDepositRequest.java @@ -71,7 +71,7 @@ public class ProcessPayDepositRequest extends TradeTask { } processModel.tradingPeer.setAccountId(nonEmptyStringOf(payDepositRequest.takerAccountId)); - processModel.setTakeOfferFeeTxId(nonEmptyStringOf(payDepositRequest.takeOfferFeeTxId)); + trade.setTakeOfferFeeTxId(nonEmptyStringOf(payDepositRequest.takeOfferFeeTxId)); processModel.setTakerAcceptedArbitratorNodeAddresses(checkNotNull(payDepositRequest.acceptedArbitratorNodeAddresses)); if (payDepositRequest.acceptedArbitratorNodeAddresses.size() < 1) failed("acceptedArbitratorNames size must be at least 1"); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java index 247aebc5cd..821cf17813 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java @@ -37,7 +37,7 @@ public class BroadcastTakeOfferFeeTx extends TradeTask { protected void run() { try { runInterceptHook(); - processModel.getTradeWalletService().broadcastTx(trade.getTakeOfferFeeTx(), + processModel.getTradeWalletService().broadcastTx(processModel.getTakeOfferFeeTx(), new FutureCallback() { @Override public void onSuccess(Transaction transaction) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/CreateTakeOfferFeeTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/CreateTakeOfferFeeTx.java index cae2540630..e731f7ed6e 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/CreateTakeOfferFeeTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/CreateTakeOfferFeeTx.java @@ -50,10 +50,8 @@ public class CreateTakeOfferFeeTx extends TradeTask { FeePolicy.getTakeOfferFee(), selectedArbitrator.getBtcAddress()); - trade.setTakeOfferFeeTx(createTakeOfferFeeTx); - - // TODO check if needed as we have stored tx already at setTakeOfferFeeTx - processModel.setTakeOfferFeeTxId(createTakeOfferFeeTx.getHashAsString()); + processModel.setTakeOfferFeeTx(createTakeOfferFeeTx); + trade.setTakeOfferFeeTxId(createTakeOfferFeeTx.getHashAsString()); complete(); } catch (Throwable t) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java index 3558a6495b..af94463592 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/SendPayDepositRequest.java @@ -38,53 +38,52 @@ public class SendPayDepositRequest extends TradeTask { protected void run() { try { runInterceptHook(); - if (trade.getTakeOfferFeeTx() != null) { - checkNotNull(trade.getTradeAmount()); - PayDepositRequest payDepositRequest = new PayDepositRequest( - processModel.getMyAddress(), - processModel.getId(), - trade.getTradeAmount().value, - processModel.getRawInputs(), - processModel.getChangeOutputValue(), - processModel.getChangeOutputAddress(), - processModel.getTradeWalletPubKey(), - processModel.getAddressEntry().getAddressString(), - processModel.getPubKeyRing(), - processModel.getPaymentAccountContractData(trade), - processModel.getAccountId(), - trade.getTakeOfferFeeTx().getHashAsString(), - processModel.getUser().getAcceptedArbitratorAddresses(), - trade.getArbitratorNodeAddress() - ); - processModel.getP2PService().sendEncryptedMailboxMessage( - trade.getTradingPeerNodeAddress(), - processModel.tradingPeer.getPubKeyRing(), - payDepositRequest, - new SendMailboxMessageListener() { - @Override - public void onArrived() { - log.trace("Message arrived at peer."); - complete(); - } + checkNotNull(trade.getTradeAmount(), "TradeAmount must not be null"); + checkNotNull(trade.getTakeOfferFeeTxId(), "TakeOfferFeeTxId must not be null"); + checkNotNull(processModel.getAddressEntry(), "AddressEntry must not be null"); - @Override - public void onStoredInMailbox() { - log.trace("Message stored in mailbox."); - complete(); - } + PayDepositRequest payDepositRequest = new PayDepositRequest( + processModel.getMyAddress(), + processModel.getId(), + trade.getTradeAmount().value, + processModel.getRawInputs(), + processModel.getChangeOutputValue(), + processModel.getChangeOutputAddress(), + processModel.getTradeWalletPubKey(), + processModel.getAddressEntry().getAddressString(), + processModel.getPubKeyRing(), + processModel.getPaymentAccountContractData(trade), + processModel.getAccountId(), + trade.getTakeOfferFeeTxId(), + processModel.getUser().getAcceptedArbitratorAddresses(), + trade.getArbitratorNodeAddress() + ); - @Override - public void onFault(String errorMessage) { - appendToErrorMessage("PayDepositRequest sending failed"); - failed(); - } + processModel.getP2PService().sendEncryptedMailboxMessage( + trade.getTradingPeerNodeAddress(), + processModel.tradingPeer.getPubKeyRing(), + payDepositRequest, + new SendMailboxMessageListener() { + @Override + public void onArrived() { + log.trace("Message arrived at peer."); + complete(); } - ); - } else { - log.error("trade.getTakeOfferFeeTx() = " + trade.getTakeOfferFeeTx()); - failed("TakeOfferFeeTx is null"); - } + + @Override + public void onStoredInMailbox() { + log.trace("Message stored in mailbox."); + complete(); + } + + @Override + public void onFault(String errorMessage) { + appendToErrorMessage("PayDepositRequest sending failed"); + failed(); + } + } + ); } catch (Throwable t) { failed(t); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java index 0060e6f27a..f918253f0d 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/VerifyAndSignContract.java @@ -30,6 +30,8 @@ import io.bitsquare.trade.protocol.trade.tasks.TradeTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static com.google.common.base.Preconditions.checkNotNull; + public class VerifyAndSignContract extends TradeTask { private static final Logger log = LoggerFactory.getLogger(VerifyAndSignContract.class); @@ -42,55 +44,53 @@ public class VerifyAndSignContract extends TradeTask { try { runInterceptHook(); - if (trade.getTakeOfferFeeTx() != null) { - TradingPeer offerer = processModel.tradingPeer; - PaymentAccountContractData offererPaymentAccountContractData = offerer.getPaymentAccountContractData(); - PaymentAccountContractData takerPaymentAccountContractData = processModel.getPaymentAccountContractData(trade); + checkNotNull(trade.getTakeOfferFeeTxId(), "TakeOfferFeeTxId must not be null"); - boolean isBuyerOffererAndSellerTaker = trade instanceof SellerAsTakerTrade; - NodeAddress buyerNodeAddress = isBuyerOffererAndSellerTaker ? processModel.getTempTradingPeerNodeAddress() : processModel.getMyAddress(); - NodeAddress sellerNodeAddress = isBuyerOffererAndSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerNodeAddress(); - log.debug("isBuyerOffererAndSellerTaker " + isBuyerOffererAndSellerTaker); - log.debug("buyerAddress " + buyerNodeAddress); - log.debug("sellerAddress " + sellerNodeAddress); + TradingPeer offerer = processModel.tradingPeer; + PaymentAccountContractData offererPaymentAccountContractData = offerer.getPaymentAccountContractData(); + PaymentAccountContractData takerPaymentAccountContractData = processModel.getPaymentAccountContractData(trade); - Contract contract = new Contract( - processModel.getOffer(), - trade.getTradeAmount(), - trade.getTakeOfferFeeTx().getHashAsString(), - buyerNodeAddress, - sellerNodeAddress, - trade.getArbitratorNodeAddress(), - isBuyerOffererAndSellerTaker, - offerer.getAccountId(), - processModel.getAccountId(), - offererPaymentAccountContractData, - takerPaymentAccountContractData, - offerer.getPubKeyRing(), - processModel.getPubKeyRing(), - offerer.getPayoutAddressString(), - processModel.getAddressEntry().getAddressString(), - offerer.getTradeWalletPubKey(), - processModel.getTradeWalletPubKey() - ); - String contractAsJson = Utilities.objectToJson(contract); - String signature = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), contractAsJson); - trade.setContract(contract); - trade.setContractAsJson(contractAsJson); - trade.setTakerContractSignature(signature); + boolean isBuyerOffererAndSellerTaker = trade instanceof SellerAsTakerTrade; + NodeAddress buyerNodeAddress = isBuyerOffererAndSellerTaker ? processModel.getTempTradingPeerNodeAddress() : processModel.getMyAddress(); + NodeAddress sellerNodeAddress = isBuyerOffererAndSellerTaker ? processModel.getMyAddress() : processModel.getTempTradingPeerNodeAddress(); + log.debug("isBuyerOffererAndSellerTaker " + isBuyerOffererAndSellerTaker); + log.debug("buyerAddress " + buyerNodeAddress); + log.debug("sellerAddress " + sellerNodeAddress); - try { - Sig.verify(offerer.getPubKeyRing().getSignaturePubKey(), - contractAsJson, - offerer.getContractSignature()); - } catch (Throwable t) { - failed("Signature verification failed. " + t.getMessage()); - } + Contract contract = new Contract( + processModel.getOffer(), + trade.getTradeAmount(), + trade.getTakeOfferFeeTxId(), + buyerNodeAddress, + sellerNodeAddress, + trade.getArbitratorNodeAddress(), + isBuyerOffererAndSellerTaker, + offerer.getAccountId(), + processModel.getAccountId(), + offererPaymentAccountContractData, + takerPaymentAccountContractData, + offerer.getPubKeyRing(), + processModel.getPubKeyRing(), + offerer.getPayoutAddressString(), + processModel.getAddressEntry().getAddressString(), + offerer.getTradeWalletPubKey(), + processModel.getTradeWalletPubKey() + ); + String contractAsJson = Utilities.objectToJson(contract); + String signature = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), contractAsJson); + trade.setContract(contract); + trade.setContractAsJson(contractAsJson); + trade.setTakerContractSignature(signature); - complete(); - } else { - failed("processModel.getTakeOfferFeeTx() = null"); + try { + Sig.verify(offerer.getPubKeyRing().getSignaturePubKey(), + contractAsJson, + offerer.getContractSignature()); + } catch (Throwable t) { + failed("Signature verification failed. " + t.getMessage()); } + + complete(); } catch (Throwable t) { failed(t); } diff --git a/gui/src/main/java/io/bitsquare/gui/components/HyperlinkWithIcon.java b/gui/src/main/java/io/bitsquare/gui/components/HyperlinkWithIcon.java index 4f7d345ef8..3928403fb6 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/HyperlinkWithIcon.java +++ b/gui/src/main/java/io/bitsquare/gui/components/HyperlinkWithIcon.java @@ -24,7 +24,7 @@ public class HyperlinkWithIcon extends AnchorPane { AnchorPane.setLeftAnchor(hyperlink, 0.0); AnchorPane.setRightAnchor(hyperlink, 15.0); AnchorPane.setRightAnchor(openLinkIcon, 4.0); - AnchorPane.setTopAnchor(openLinkIcon, 3.0); + AnchorPane.setTopAnchor(openLinkIcon, awesomeIcon == AwesomeIcon.INFO_SIGN ? 2.0 : 3.0); getChildren().addAll(hyperlink, openLinkIcon); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainView.java b/gui/src/main/java/io/bitsquare/gui/main/MainView.java index 75384cb28c..ed4dd0b056 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainView.java @@ -124,10 +124,14 @@ public class MainView extends InitializableView { Tuple2 availableBalanceBox = getBalanceBox("Available balance"); availableBalanceBox.first.textProperty().bind(model.availableBalance); + Tuple2 reservedBalanceBox = getBalanceBox("Reserved balance"); + reservedBalanceBox.first.textProperty().bind(model.reservedBalance); + Tuple2 lockedBalanceBox = getBalanceBox("Locked balance"); lockedBalanceBox.first.textProperty().bind(model.lockedBalance); - HBox rightNavPane = new HBox(availableBalanceBox.second, lockedBalanceBox.second, settingsButton, accountButton) {{ + HBox rightNavPane = new HBox(availableBalanceBox.second, reservedBalanceBox.second, lockedBalanceBox.second, + settingsButton, accountButton) {{ setSpacing(10); setRightAnchor(this, 10d); setTopAnchor(this, 0d); diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java index a78fe44328..e430748b1d 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -26,10 +26,7 @@ import io.bitsquare.app.Version; import io.bitsquare.arbitration.ArbitratorManager; import io.bitsquare.arbitration.Dispute; import io.bitsquare.arbitration.DisputeManager; -import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.BitcoinNetwork; -import io.bitsquare.btc.TradeWalletService; -import io.bitsquare.btc.WalletService; +import io.bitsquare.btc.*; import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.common.UserThread; import io.bitsquare.gui.common.model.ViewModel; @@ -94,6 +91,7 @@ public class MainViewModel implements ViewModel { final StringProperty walletServiceErrorMsg = new SimpleStringProperty(); final StringProperty btcSplashSyncIconId = new SimpleStringProperty(); final StringProperty availableBalance = new SimpleStringProperty(); + final StringProperty reservedBalance = new SimpleStringProperty(); final StringProperty lockedBalance = new SimpleStringProperty(); // P2P network @@ -493,21 +491,42 @@ public class MainViewModel implements ViewModel { private void updateBalance() { updateAvailableBalance(); + updateReservedBalance(); updateLockedBalance(); } + private void updateReservedBalance() { + Coin sum = Coin.valueOf(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream()) + .map(tradable -> walletService.getAddressEntryByOfferId(tradable.getId())) + .map(addressEntry -> walletService.getBalanceForAddress(addressEntry.getAddress())) + .mapToLong(Coin::getValue) + .sum()); + reservedBalance.set(formatter.formatCoinWithCode(sum)); + } + private void updateLockedBalance() { - List result = new ArrayList<>(); - - result.addAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream()) - .map(tradable -> walletService.getAddressEntryByOfferId(tradable.getOffer().getId())) - .collect(Collectors.toList())); - - Optional totalLockedOptional = result.stream().map(e -> walletService.getBalanceForAddress(e.getAddress())).reduce((a, b) -> a.add(b)); - if (totalLockedOptional.isPresent()) - lockedBalance.set(formatter.formatCoinWithCode(totalLockedOptional.get())); - else - lockedBalance.set(formatter.formatCoinWithCode(Coin.ZERO)); + Coin sum = Coin.valueOf(tradeManager.getTrades().stream() + .map(trade -> { + switch (trade.getState().getPhase()) { + case DEPOSIT_REQUESTED: + case DEPOSIT_PAID: + case FIAT_SENT: + case FIAT_RECEIVED: + Coin balanceInDeposit = FeePolicy.getSecurityDeposit().add(FeePolicy.getFeePerKb()); + if (trade.getContract() != null && + trade.getTradeAmount() != null && + trade.getContract().getSellerPayoutAddressString() + .equals(walletService.getAddressEntryByOfferId(trade.getId()).getAddressString())) { + balanceInDeposit = balanceInDeposit.add(trade.getTradeAmount()); + } + return balanceInDeposit; + default: + return Coin.ZERO; + } + }) + .mapToLong(Coin::getValue) + .sum()); + lockedBalance.set(formatter.formatCoinWithCode(sum)); } private void updateAvailableBalance() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/FundsView.fxml b/gui/src/main/java/io/bitsquare/gui/main/funds/FundsView.fxml index b6855385cf..8164543468 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/FundsView.fxml +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/FundsView.fxml @@ -24,8 +24,8 @@ AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml"> - - + + \ No newline at end of file diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedListItem.java b/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedListItem.java index a18d634715..c46e303589 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedListItem.java +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedListItem.java @@ -20,19 +20,16 @@ package io.bitsquare.gui.main.funds.reserved; import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.WalletService; -import io.bitsquare.btc.listeners.AddressConfidenceListener; import io.bitsquare.btc.listeners.BalanceListener; -import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator; import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.trade.Tradable; import io.bitsquare.trade.Trade; +import io.bitsquare.trade.offer.OpenOffer; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; -import org.bitcoinj.core.TransactionConfidence; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,19 +38,12 @@ public class ReservedListItem { private final StringProperty date = new SimpleStringProperty(); private final BalanceListener balanceListener; - private final Label balanceLabel; - + private String fundsInfo; private final Tradable tradable; private final AddressEntry addressEntry; - private final WalletService walletService; private final BSFormatter formatter; - private final AddressConfidenceListener confidenceListener; - - private final ConfidenceProgressIndicator progressIndicator; - - private final Tooltip tooltip; private final String addressString; private Coin balance; @@ -64,25 +54,7 @@ public class ReservedListItem { this.formatter = formatter; addressString = addressEntry.getAddressString(); - // confidence - progressIndicator = new ConfidenceProgressIndicator(); - progressIndicator.setId("funds-confidence"); - tooltip = new Tooltip("Not used yet"); - progressIndicator.setProgress(0); - progressIndicator.setPrefSize(24, 24); - Tooltip.install(progressIndicator, tooltip); - - confidenceListener = walletService.addAddressConfidenceListener(new AddressConfidenceListener(getAddress()) { - @Override - public void onTransactionConfidenceChanged(TransactionConfidence confidence) { - updateConfidence(confidence); - } - }); - - updateConfidence(walletService.getConfidenceForAddress(getAddress())); - - //date.set(formatter.formatDateTime(transaction.getUpdateTime())); - + date.set(formatter.formatDateTime(tradable.getDate())); // balance balanceLabel = new Label(); @@ -97,113 +69,67 @@ public class ReservedListItem { } public void cleanup() { - walletService.removeAddressConfidenceListener(confidenceListener); walletService.removeBalanceListener(balanceListener); } private void updateBalance(Coin balance) { this.balance = balance; if (balance != null) { + balanceLabel.setText(formatter.formatCoin(balance)); + if (tradable instanceof Trade) { Trade trade = (Trade) tradable; Trade.Phase phase = trade.getState().getPhase(); switch (phase) { case PREPARATION: case TAKER_FEE_PAID: - balanceLabel.setText(formatter.formatCoinWithCode(balance) + " (locally reserved)"); + fundsInfo = "Reserved in local wallet"; break; case DEPOSIT_REQUESTED: case DEPOSIT_PAID: case FIAT_SENT: case FIAT_RECEIVED: + fundsInfo = "Locked in MultiSig"; // We ignore the tx fee as it will be paid by both (once deposit, once payout) - Coin balanceInDeposit = FeePolicy.getSecurityDeposit(); + Coin balanceInDeposit = FeePolicy.getSecurityDeposit().add(FeePolicy.getFeePerKb()); // For the seller we add the trade amount if (trade.getContract() != null && trade.getTradeAmount() != null && trade.getContract().getSellerPayoutAddressString().equals(addressString)) balanceInDeposit = balanceInDeposit.add(trade.getTradeAmount()); - balanceLabel.setText(formatter.formatCoinWithCode(balanceInDeposit) + " (in MS escrow)"); + balanceLabel.setText(formatter.formatCoin(balanceInDeposit)); break; case PAYOUT_PAID: - balanceLabel.setText(formatter.formatCoinWithCode(balance) + " (in local wallet)"); + fundsInfo = "Received in local wallet"; break; case WITHDRAWN: log.error("Invalid state at updateBalance (WITHDRAWN)"); - balanceLabel.setText(formatter.formatCoinWithCode(balance) + " already withdrawn"); break; case DISPUTE: - balanceLabel.setText(formatter.formatCoinWithCode(balance) + " open dispute/ticket"); + log.error("Invalid state at updateBalance (DISPUTE)"); break; default: log.warn("Not supported tradePhase: " + phase); } - - } else - balanceLabel.setText(formatter.formatCoin(balance)); - } - } - - private void updateConfidence(TransactionConfidence confidence) { - if (confidence != null) { - //log.debug("Type numBroadcastPeers getDepthInBlocks " + confidence.getConfidenceType() + " / " + - // confidence.numBroadcastPeers() + " / " + confidence.getDepthInBlocks()); - switch (confidence.getConfidenceType()) { - case UNKNOWN: - tooltip.setText("Unknown transaction status"); - progressIndicator.setProgress(0); - break; - case PENDING: - tooltip.setText("Seen by " + confidence.numBroadcastPeers() + " peer(s) / 0 confirmations"); - progressIndicator.setProgress(-1.0); - break; - case BUILDING: - tooltip.setText("Confirmed in " + confidence.getDepthInBlocks() + " block(s)"); - progressIndicator.setProgress(Math.min(1, (double) confidence.getDepthInBlocks() / 6.0)); - break; - case DEAD: - tooltip.setText("Transaction is invalid."); - progressIndicator.setProgress(0); - break; + } else if (tradable instanceof OpenOffer) { + fundsInfo = "Reserved in local wallet"; } } } - - public final String getLabel() { - switch (addressEntry.getContext()) { - case TRADE: - if (tradable instanceof Trade) - return "Trade ID: " + addressEntry.getShortOfferId(); - else - return "Offer ID: " + addressEntry.getShortOfferId(); - case ARBITRATOR: - return "Arbitration deposit"; - } - return ""; - } - private Address getAddress() { return addressEntry.getAddress(); } - public AddressEntry getAddressEntry() { return addressEntry; } - - public ConfidenceProgressIndicator getProgressIndicator() { - return progressIndicator; - } - - public Label getBalanceLabel() { return balanceLabel; } - public Coin getBalance() { return balance; } @@ -211,4 +137,8 @@ public class ReservedListItem { public String getAddressString() { return addressString; } + + public String getFundsInfo() { + return fundsInfo; + } } diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.fxml b/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.fxml index 75b766f6a8..2c32179467 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.fxml +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.fxml @@ -29,15 +29,14 @@ - + + + - + - - - - + diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.java b/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.java index 01bb8d8a1f..917326a506 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/reserved/ReservedView.java @@ -17,15 +17,19 @@ package io.bitsquare.gui.main.funds.reserved; +import de.jensd.fx.fontawesome.AwesomeIcon; +import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.WalletService; import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.common.util.Utilities; import io.bitsquare.gui.common.view.ActivatableView; import io.bitsquare.gui.common.view.FxmlView; +import io.bitsquare.gui.components.HyperlinkWithIcon; import io.bitsquare.gui.popups.OfferDetailsPopup; import io.bitsquare.gui.popups.Popup; import io.bitsquare.gui.popups.TradeDetailsPopup; import io.bitsquare.gui.util.BSFormatter; +import io.bitsquare.trade.Tradable; import io.bitsquare.trade.Trade; import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.offer.OpenOffer; @@ -59,7 +63,8 @@ public class ReservedView extends ActivatableView { private final BSFormatter formatter; private final OfferDetailsPopup offerDetailsPopup; private final TradeDetailsPopup tradeDetailsPopup; - private final ObservableList addressList = FXCollections.observableArrayList(); + private final ObservableList reservedAddresses = FXCollections.observableArrayList(); + private BalanceListener balanceListener; /////////////////////////////////////////////////////////////////////////////////////////// @@ -82,30 +87,31 @@ public class ReservedView extends ActivatableView { @Override public void initialize() { table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - table.setPlaceholder(new Label("No funded are reserved in open offers or trades")); - - setLabelColumnCellFactory(); + table.setPlaceholder(new Label("No funds are reserved in open offers or trades")); + setDateColumnCellFactory(); + setDetailsColumnCellFactory(); setAddressColumnCellFactory(); setBalanceColumnCellFactory(); - setConfidenceColumnCellFactory(); + table.getSortOrder().add(dateColumn); + balanceListener = new BalanceListener() { + @Override + public void onBalanceChanged(Coin balance) { + updateList(); + } + }; } @Override protected void activate() { updateList(); - table.setItems(addressList); - walletService.addBalanceListener(new BalanceListener() { - @Override - public void onBalanceChanged(Coin balance) { - updateList(); - } - }); + walletService.addBalanceListener(balanceListener); } @Override protected void deactivate() { - addressList.forEach(ReservedListItem::cleanup); + reservedAddresses.forEach(ReservedListItem::cleanup); + walletService.removeBalanceListener(balanceListener); } @@ -114,10 +120,13 @@ public class ReservedView extends ActivatableView { /////////////////////////////////////////////////////////////////////////////////////////// private void updateList() { - addressList.forEach(ReservedListItem::cleanup); - addressList.setAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream()) + reservedAddresses.forEach(ReservedListItem::cleanup); + reservedAddresses.setAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream()) .map(tradable -> new ReservedListItem(tradable, walletService.getAddressEntryByOfferId(tradable.getOffer().getId()), walletService, formatter)) .collect(Collectors.toList())); + + reservedAddresses.sort((o1, o2) -> getTradable(o2).get().getDate().compareTo(getTradable(o1).get().getDate())); + table.setItems(reservedAddresses); } private void openBlockExplorer(ReservedListItem item) { @@ -130,47 +139,115 @@ public class ReservedView extends ActivatableView { } } + private Optional getTradable(ReservedListItem item) { + String offerId = item.getAddressEntry().getOfferId(); + Optional tradeOptional = tradeManager.getTradeById(offerId); + if (tradeOptional.isPresent()) { + return Optional.of(tradeOptional.get()); + } else if (openOfferManager.getOpenOfferById(offerId).isPresent()) { + return Optional.of(openOfferManager.getOpenOfferById(offerId).get()); + } else { + return Optional.empty(); + } + } + + private void openDetailPopup(ReservedListItem item) { + Optional tradableOptional = getTradable(item); + if (tradableOptional.isPresent()) { + Tradable tradable = tradableOptional.get(); + if (tradable instanceof Trade) { + tradeDetailsPopup.show((Trade) tradable); + } else if (tradable instanceof OpenOffer) { + offerDetailsPopup.show(tradable.getOffer()); + } + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // ColumnCellFactories /////////////////////////////////////////////////////////////////////////////////////////// - private void setLabelColumnCellFactory() { - detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); - detailsColumn.setCellFactory(new Callback, - TableCell>() { + private void setDateColumnCellFactory() { + dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + dateColumn.setCellFactory(new Callback, + TableCell>() { @Override public TableCell call(TableColumn column) { return new TableCell() { - private Hyperlink hyperlink; + @Override + public void updateItem(final ReservedListItem item, boolean empty) { + super.updateItem(item, empty); + if (item != null && !empty) { + if (getTradable(item).isPresent()) + setText(formatter.formatDateTime(getTradable(item).get().getDate())); + else + setText("No date available"); + } else { + setText(""); + } + } + }; + } + }); + } + + private void setDetailsColumnCellFactory() { + detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + detailsColumn.setCellFactory(new Callback, + TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + + private HyperlinkWithIcon field; @Override public void updateItem(final ReservedListItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { - hyperlink = new Hyperlink(item.getLabel()); - if (item.getAddressEntry().getOfferId() != null) { - Tooltip tooltip = new Tooltip(item.getAddressEntry().getOfferId()); - Tooltip.install(hyperlink, tooltip); + Optional tradableOptional = getTradable(item); + if (tradableOptional.isPresent()) { + AddressEntry addressEntry = item.getAddressEntry(); + String details; + if (addressEntry.getContext() == AddressEntry.Context.TRADE) { + String prefix; + Tradable tradable = tradableOptional.get(); + if (tradable instanceof Trade) + prefix = "Trade ID: "; + else if (tradable instanceof OpenOffer) + prefix = "Offer ID: "; + else + prefix = ""; - hyperlink.setOnAction(event -> { - Optional tradeOptional = tradeManager.getTradeById(item.getAddressEntry().getOfferId()); - Optional openOfferOptional = openOfferManager.getOpenOfferById(item.getAddressEntry().getOfferId()); - if (tradeOptional.isPresent()) - tradeDetailsPopup.show(tradeOptional.get()); - else if (openOfferOptional.isPresent()) - offerDetailsPopup.show(openOfferOptional.get().getOffer()); - }); + details = prefix + addressEntry.getShortOfferId(); + } else if (addressEntry.getContext() == AddressEntry.Context.ARBITRATOR) { + details = "Arbitration fee"; + } else { + details = "-"; + } + + field = new HyperlinkWithIcon(details + " (" + item.getFundsInfo() + ")", + AwesomeIcon.INFO_SIGN); + field.setOnAction(event -> openDetailPopup(item)); + field.setTooltip(new Tooltip("Open popup for details")); + setGraphic(field); + } else if (item.getAddressEntry().getContext() == AddressEntry.Context.ARBITRATOR) { + setGraphic(new Label("Arbitrators fee")); + } else { + setGraphic(new Label("No details available")); } - setGraphic(hyperlink); + } else { setGraphic(null); - setId(null); + if (field != null) + field.setOnAction(null); } } }; @@ -188,19 +265,23 @@ public class ReservedView extends ActivatableView { public TableCell call(TableColumn column) { return new TableCell() { - private Hyperlink hyperlink; + private HyperlinkWithIcon hyperlinkWithIcon; @Override public void updateItem(final ReservedListItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { - hyperlink = new Hyperlink(item.getAddressString()); - hyperlink.setOnAction(event -> openBlockExplorer(item)); - setGraphic(hyperlink); + String address = item.getAddressString(); + hyperlinkWithIcon = new HyperlinkWithIcon(address, AwesomeIcon.EXTERNAL_LINK); + hyperlinkWithIcon.setOnAction(event -> openBlockExplorer(item)); + hyperlinkWithIcon.setTooltip(new Tooltip("Open external blockchain explorer for " + + "address: " + address)); + setGraphic(hyperlinkWithIcon); } else { setGraphic(null); - setId(null); + if (hyperlinkWithIcon != null) + hyperlinkWithIcon.setOnAction(null); } } }; @@ -228,33 +309,6 @@ public class ReservedView extends ActivatableView { }); } - private void setConfidenceColumnCellFactory() { - confidenceColumn.setCellValueFactory((addressListItem) -> - new ReadOnlyObjectWrapper<>(addressListItem.getValue())); - confidenceColumn.setCellFactory( - new Callback, TableCell>() { - - @Override - public TableCell call(TableColumn column) { - return new TableCell() { - - @Override - public void updateItem(final ReservedListItem item, boolean empty) { - super.updateItem(item, empty); - - if (item != null && !empty) { - setGraphic(item.getProgressIndicator()); - } else { - setGraphic(null); - } - } - }; - } - }); - } - } diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsListItem.java b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsListItem.java index 28eec18ba8..b654bad6c3 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsListItem.java +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsListItem.java @@ -113,7 +113,7 @@ public class TransactionsListItem { details = "Create offer fee: " + tradable.getShortId(); } else if (tradable instanceof Trade) { Trade trade = (Trade) tradable; - if (trade.getTakeOfferFeeTx() != null && trade.getTakeOfferFeeTx().getHashAsString().equals(txId)) { + if (trade.getTakeOfferFeeTxId() != null && trade.getTakeOfferFeeTxId().equals(txId)) { details = "Take offer fee: " + tradable.getShortId(); } else if (trade.getOffer() != null && trade.getOffer().getOfferFeePaymentTxID() != null && diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.fxml b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.fxml index a1d53ec0db..5cae4d66eb 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.fxml +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.fxml @@ -36,7 +36,7 @@ - + diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java index 5956cd7331..9e2e0f55eb 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java @@ -73,7 +73,7 @@ public class TransactionsView extends ActivatableView { private final BSFormatter formatter; private final Preferences preferences; private final TradeDetailsPopup tradeDetailsPopup; - private DisputeManager disputeManager; + private final DisputeManager disputeManager; private final OfferDetailsPopup offerDetailsPopup; private WalletEventListener walletEventListener; @@ -109,7 +109,7 @@ public class TransactionsView extends ActivatableView { setTransactionColumnCellFactory(); setConfidenceColumnCellFactory(); table.getSortOrder().add(dateColumn); - + walletEventListener = new WalletEventListener() { @Override public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { @@ -150,7 +150,6 @@ public class TransactionsView extends ActivatableView { @Override protected void activate() { updateList(); - table.setItems(transactionsListItems); walletService.getWallet().addEventListener(walletEventListener); } @@ -169,19 +168,19 @@ public class TransactionsView extends ActivatableView { Stream concat1 = Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream()); Stream concat2 = Stream.concat(concat1, closedTradableManager.getClosedTrades().stream()); Stream concat3 = Stream.concat(concat2, failedTradesManager.getFailedTrades().stream()); - Set allTradables = concat3.collect(Collectors.toSet()); + Set concated = concat3.collect(Collectors.toSet()); List listItems = walletService.getWallet().getRecentTransactions(1000, true).stream() .map(transaction -> { - Optional tradableOptional = allTradables.stream() + Optional tradableOptional = concated.stream() .filter(e -> { String txId = transaction.getHashAsString(); if (e instanceof OpenOffer) return e.getOffer().getOfferFeePaymentTxID().equals(txId); else if (e instanceof Trade) { Trade trade = (Trade) e; - return (trade.getTakeOfferFeeTx() != null && - trade.getTakeOfferFeeTx().getHashAsString().equals(txId)) || + return (trade.getTakeOfferFeeTxId() != null && + trade.getTakeOfferFeeTxId().equals(txId)) || (trade.getOffer() != null && trade.getOffer().getOfferFeePaymentTxID() != null && trade.getOffer().getOfferFeePaymentTxID().equals(txId)) || @@ -205,6 +204,7 @@ public class TransactionsView extends ActivatableView { // are sorted by getRecentTransactions transactionsListItems.forEach(TransactionsListItem::cleanup); transactionsListItems.setAll(listItems); + table.setItems(transactionsListItems); } private void openBlockExplorer(TransactionsListItem item) { diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.fxml b/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.fxml index 1330b24f17..8c6b353992 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.fxml +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.fxml @@ -36,7 +36,7 @@ - + diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.java b/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.java index acb4cf0142..281ed65d4b 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/withdrawal/WithdrawalView.java @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.FutureCallback; import de.jensd.fx.fontawesome.AwesomeIcon; import io.bitsquare.app.BitsquareApp; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.Restrictions; import io.bitsquare.btc.WalletService; import io.bitsquare.btc.listeners.BalanceListener; @@ -92,6 +91,8 @@ public class WithdrawalView extends ActivatableView { private final TradeDetailsPopup tradeDetailsPopup; private final ObservableList fundedAddresses = FXCollections.observableArrayList(); private Set selectedItems = new HashSet<>(); + private BalanceListener balanceListener; + private Set fromAddresses; /////////////////////////////////////////////////////////////////////////////////////////// @@ -121,7 +122,7 @@ public class WithdrawalView extends ActivatableView { @Override public void initialize() { table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - table.setPlaceholder(new Label("No funds for withdrawal available")); + table.setPlaceholder(new Label("No funds for withdrawal are available")); setDateColumnCellFactory(); setDetailsColumnCellFactory(); setAddressColumnCellFactory(); @@ -129,19 +130,22 @@ public class WithdrawalView extends ActivatableView { setSelectColumnCellFactory(); table.getSortOrder().add(dateColumn); table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + + balanceListener = new BalanceListener() { + @Override + public void onBalanceChanged(Coin balance) { + updateList(); + } + }; } @Override protected void activate() { updateList(); - table.setItems(fundedAddresses); + reset(); - walletService.addBalanceListener(new BalanceListener() { - @Override - public void onBalanceChanged(Coin balance) { - updateList(); - } - }); + + walletService.addBalanceListener(balanceListener); withdrawButton.disableProperty().bind(Bindings.createBooleanBinding(() -> !areInputsValid(), amountTextField.textProperty(), withdrawToTextField.textProperty())); } @@ -150,6 +154,7 @@ public class WithdrawalView extends ActivatableView { protected void deactivate() { fundedAddresses.forEach(WithdrawalListItem::cleanup); withdrawButton.disableProperty().unbind(); + walletService.removeBalanceListener(balanceListener); } @@ -160,7 +165,7 @@ public class WithdrawalView extends ActivatableView { @FXML public void onWithdraw() { Coin senderAmount = formatter.parseToCoin(amountTextField.getText()); - if (Restrictions.isAboveDust(senderAmount)) { + if (Restrictions.isAboveFixedTxFeeAndDust(senderAmount)) { FutureCallback callback = new FutureCallback() { @Override public void onSuccess(@javax.annotation.Nullable Transaction transaction) { @@ -176,33 +181,32 @@ public class WithdrawalView extends ActivatableView { log.error("onWithdraw onFailure"); } }; - // try { - /* Coin requiredFee = walletService.getRequiredFee(withdrawFromTextField.getText(), - withdrawToTextField.getText(), senderAmount, null);*/ - // TODO static fee might be not enough when using many inputs, but for now its high enough to get probably into the blockchain - // Use bitcoinJ fee calculation instead.... - Coin requiredFee = FeePolicy.getFeePerKb(); - Coin receiverAmount = senderAmount.subtract(requiredFee); - if (BitsquareApp.DEV_MODE) { - doWithdraw(receiverAmount, callback); - } else { - new Popup().headLine("Confirm your withdrawal request") - .message("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" + - "From address: " + withdrawFromTextField.getText() + "\n" + - "To receiving address: " + withdrawToTextField.getText() + ".\n\n" + - "Required transaction fee is: " + formatter.formatCoinWithCode(requiredFee) + "\n" + - "Recipient will receive: " + formatter.formatCoinWithCode(receiverAmount) + "\n\n" + - "Are you sure you want to withdraw that amount?") - .onAction(() -> doWithdraw(receiverAmount, callback)) - .show(); + try { + Coin requiredFee = walletService.getRequiredFeeForMultipleAddresses(fromAddresses, + withdrawToTextField.getText(), senderAmount, null); + Coin receiverAmount = senderAmount.subtract(requiredFee); + if (BitsquareApp.DEV_MODE) { + doWithdraw(receiverAmount, callback); + } else { + new Popup().headLine("Confirm your withdrawal request") + .message("Sending: " + formatter.formatCoinWithCode(senderAmount) + "\n" + + "From address: " + withdrawFromTextField.getText() + "\n" + + "To receiving address: " + withdrawToTextField.getText() + ".\n\n" + + "Required transaction fee is: " + formatter.formatCoinWithCode(requiredFee) + "\n" + + "Recipient will receive: " + formatter.formatCoinWithCode(receiverAmount) + "\n\n" + + "Are you sure you want to withdraw that amount?") + .onAction(() -> doWithdraw(receiverAmount, callback)) + .show(); - } - /*} catch (AddressFormatException | InsufficientMoneyException e) { + } + } catch (AddressFormatException | InsufficientMoneyException 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.").show(); + new Popup() + .warning("The amount to transfer is lower than the transaction fee and the min. possible tx value (dust).") + .show(); } } @@ -212,6 +216,10 @@ public class WithdrawalView extends ActivatableView { else selectedItems.remove(item); + fromAddresses = selectedItems.stream() + .map(WithdrawalListItem::getAddressString) + .collect(Collectors.toSet()); + if (!selectedItems.isEmpty()) { Coin sum = Coin.valueOf(selectedItems.stream().mapToLong(e -> e.getBalance().getValue()).sum()); if (sum.isPositive()) { @@ -225,10 +233,9 @@ public class WithdrawalView extends ActivatableView { withdrawFromTextField.setText(selectedItems.stream().findAny().get().getAddressEntry().getAddressString()); withdrawFromTextField.setTooltip(null); } else { - //selectedItems.stream(). String tooltipText = "Withdraw from multiple addresses:\n" + selectedItems.stream() - .map(e -> e.getAddressString()) + .map(WithdrawalListItem::getAddressString) .collect(Collectors.joining(",\n")); int abbr = Math.max(10, 66 / selectedItems.size()); String text = "Withdraw from multiple addresses (" + @@ -299,6 +306,7 @@ public class WithdrawalView extends ActivatableView { return date2.compareTo(date1); }); + table.setItems(fundedAddresses); } private void doWithdraw(Coin amount, FutureCallback callback) { @@ -306,19 +314,13 @@ public class WithdrawalView extends ActivatableView { walletPasswordPopup.show().onAesKey(aesKey -> sendFunds(amount, aesKey, callback)); else sendFunds(amount, null, callback); - updateList(); } private void sendFunds(Coin amount, KeyParameter aesKey, FutureCallback callback) { try { - Set fromAddresses = selectedItems.stream() - .map(e -> e.getAddressString()) - .collect(Collectors.toSet()); - if (!fromAddresses.isEmpty()) { - walletService.sendFundsForMultipleAddresses(fromAddresses, withdrawToTextField.getText(), amount, null, aesKey, callback); - - reset(); - } + walletService.sendFundsForMultipleAddresses(fromAddresses, withdrawToTextField.getText(), amount, null, aesKey, callback); + reset(); + updateList(); } catch (AddressFormatException e) { new Popup().error("The address is not correct. Please check the address format.").show(); } catch (InsufficientMoneyException e) { @@ -346,7 +348,6 @@ public class WithdrawalView extends ActivatableView { withdrawToTextField.setText("mxAkWWaQBqwqcYstKzqLku3kzR6pbu2zHq"); } - private Optional getTradable(WithdrawalListItem item) { String offerId = item.getAddressEntry().getOfferId(); Optional tradableOptional = closedTradableManager.getTradableById(offerId); @@ -359,12 +360,6 @@ public class WithdrawalView extends ActivatableView { } } - private boolean isTradableAvailable(WithdrawalListItem item) { - String offerId = item.getAddressEntry().getOfferId(); - return closedTradableManager.getTradableById(offerId).isPresent() || - failedTradesManager.getTradeById(offerId).isPresent(); - } - private boolean areInputsValid() { return btcAddressValidator.validate(withdrawToTextField.getText()).isValid && amountTextField.getText().length() > 0 && @@ -420,22 +415,19 @@ public class WithdrawalView extends ActivatableView { super.updateItem(item, empty); if (item != null && !empty) { - if (isTradableAvailable(item)) { + Optional tradableOptional = getTradable(item); + if (tradableOptional.isPresent()) { AddressEntry addressEntry = item.getAddressEntry(); String details; if (addressEntry.getContext() == AddressEntry.Context.TRADE) { String prefix; - if (getTradable(item).isPresent()) { - Tradable tradable = getTradable(item).get(); - if (tradable instanceof Trade) - prefix = "Trade ID: "; - else if (tradable instanceof OpenOffer) - prefix = "Offer ID: "; - else - prefix = ""; - } else { + Tradable tradable = tradableOptional.get(); + if (tradable instanceof Trade) + prefix = "Trade ID: "; + else if (tradable instanceof OpenOffer) + prefix = "Offer ID: "; + else prefix = ""; - } details = prefix + addressEntry.getShortOfferId(); } else if (addressEntry.getContext() == AddressEntry.Context.ARBITRATOR) { diff --git a/gui/src/main/java/io/bitsquare/gui/popups/TradeDetailsPopup.java b/gui/src/main/java/io/bitsquare/gui/popups/TradeDetailsPopup.java index 7add8396f0..b66e23efed 100644 --- a/gui/src/main/java/io/bitsquare/gui/popups/TradeDetailsPopup.java +++ b/gui/src/main/java/io/bitsquare/gui/popups/TradeDetailsPopup.java @@ -121,11 +121,10 @@ public class TradeDetailsPopup extends Popup { if (buyerPaymentAccountContractData == null && sellerPaymentAccountContractData == null) rows++; - - if (trade.getTakeOfferFeeTx() != null) - rows++; } + if (trade.getTakeOfferFeeTxId() != null) + rows++; if (trade.getDepositTx() != null) rows++; if (trade.getPayoutTx() != null) @@ -156,8 +155,8 @@ public class TradeDetailsPopup extends Popup { } addLabelTxIdTextField(gridPane, ++rowIndex, "Offer fee transaction ID:", offer.getOfferFeePaymentTxID()); - if (contract != null && trade.getTakeOfferFeeTx() != null) - addLabelTxIdTextField(gridPane, ++rowIndex, "Taker fee transaction ID:", trade.getTakeOfferFeeTx().getHashAsString()); + if (trade.getTakeOfferFeeTxId() != null) + addLabelTxIdTextField(gridPane, ++rowIndex, "Taker fee transaction ID:", trade.getTakeOfferFeeTxId()); if (trade.getDepositTx() != null) addLabelTxIdTextField(gridPane, ++rowIndex, "Deposit transaction ID:", trade.getDepositTx().getHashAsString());