From ccd2e6b6768456bb91be5f550afc449bfdbd29ef Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 26 Mar 2015 11:51:42 +0100 Subject: [PATCH] Fixed state handling in views --- .../closed/ClosedTradesViewModel.java | 7 +- .../pending/PendingTradesDataModel.java | 114 +++++-------- .../portfolio/pending/PendingTradesView.java | 44 +++-- .../pending/PendingTradesViewModel.java | 160 +++++++++++------- .../io/bitsquare/storage/FileManager.java | 1 + .../java/io/bitsquare/storage/Storage.java | 4 +- .../java/io/bitsquare/trade/OffererTrade.java | 4 +- .../main/java/io/bitsquare/trade/Trade.java | 6 +- .../java/io/bitsquare/trade/TradeManager.java | 58 ++++++- .../src/main/java/io/bitsquare/user/User.java | 5 +- 10 files changed, 235 insertions(+), 168 deletions(-) diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesViewModel.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesViewModel.java index d771acf7a0..503d1305bb 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesViewModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesViewModel.java @@ -78,7 +78,7 @@ class ClosedTradesViewModel extends ActivatableWithDataModel tradeStateChangeListener; private final ListChangeListener tradesListChangeListener; final StringProperty txId = new SimpleStringProperty(); - final ObjectProperty tradeState = new SimpleObjectProperty<>(); final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1); + final ObjectProperty takerProcessState = new SimpleObjectProperty<>(); + final ObjectProperty offererProcessState = new SimpleObjectProperty<>(); + @Inject public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, User user) { this.tradeManager = tradeManager; this.walletService = walletService; this.user = user; - tradeStateChangeListener = (ov, oldValue, newValue) -> tradeState.set(newValue); - tradesListChangeListener = change -> applyList(); + tradesListChangeListener = change -> onListChanged(); } @Override public void activate() { - applyList(); + onListChanged(); tradeManager.getPendingTrades().addListener(tradesListChangeListener); if (list.size() > 0) { @@ -98,10 +91,10 @@ class PendingTradesDataModel implements Activatable, DataModel { @Override public void deactivate() { tradeManager.getPendingTrades().removeListener(tradesListChangeListener); - cleanUpSelectedTrade(); + removeListenerFromSelectedTrade(); } - private void applyList() { + private void onListChanged() { list.clear(); list.addAll(tradeManager.getPendingTrades().stream().map(PendingTradesListItem::new).collect(Collectors.toList())); @@ -116,7 +109,7 @@ class PendingTradesDataModel implements Activatable, DataModel { void selectTrade(PendingTradesListItem item) { // clean up previous selectedItem - cleanUpSelectedTrade(); + removeListenerFromSelectedTrade(); selectedItem = item; @@ -124,8 +117,11 @@ class PendingTradesDataModel implements Activatable, DataModel { isOfferer = getTrade().getOffer().getP2PSigPubKey().equals(user.getP2PSigPubKey()); Trade trade = getTrade(); - trade.processStateProperty().addListener(tradeStateChangeListener); - tradeState.set(trade.processStateProperty().get()); + if (trade instanceof TakerTrade) + takerProcessState.bind(((TakerTrade) trade).processStateProperty()); + else + offererProcessState.bind(((OffererTrade) trade).processStateProperty()); + log.trace("selectTrade trade.stateProperty().get() " + trade.processStateProperty().get()); if (trade.getDepositTx() != null) @@ -133,7 +129,6 @@ class PendingTradesDataModel implements Activatable, DataModel { } else { txId.set(null); - tradeState.set(null); } } @@ -146,34 +141,14 @@ class PendingTradesDataModel implements Activatable, DataModel { } void withdraw(String toAddress) { - FutureCallback callback = new FutureCallback() { - @Override - public void onSuccess(@javax.annotation.Nullable Transaction transaction) { - if (transaction != null) { - log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString()); - - if (closedTrade != null) { - list.removeIf(e -> e.getTrade().getId().equals(closedTrade.getId())); - } - } - } - - @Override - public void onFailure(@NotNull Throwable t) { - log.debug("onWithdraw onFailure"); - } - }; - - AddressEntry addressEntry = walletService.getAddressEntry(getTrade().getId()); - String fromAddress = addressEntry.getAddressString(); - try { - walletService.sendFunds(fromAddress, toAddress, getAmountToWithdraw(), callback); - } catch (AddressFormatException | InsufficientMoneyException e) { - e.printStackTrace(); - log.error(e.getMessage()); - } - - tradeManager.onWithdrawAtTradeCompleted(getTrade()); + tradeManager.requestWithdraw(toAddress, + getTrade(), + () -> log.debug("requestWithdraw was successful"), + (errorMessage, throwable) -> { + log.error(errorMessage); + Popups.openExceptionPopup(throwable); + }); + /* Action response = Popups.openConfirmPopup( @@ -203,7 +178,6 @@ class PendingTradesDataModel implements Activatable, DataModel { }*/ } - ObservableList getList() { return list; } @@ -232,31 +206,35 @@ class PendingTradesDataModel implements Activatable, DataModel { return selectedItem.getTrade().getOffer().getCurrencyCode(); } + Throwable getTradeException() { + return getTrade().getThrowable(); + } + + String getErrorMessage() { + return getTrade().getErrorMessage(); + } + public Offer.Direction getDirection(Offer offer) { return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ? offer.getDirection() : offer.getMirroredDirection(); } - Coin getAmountToWithdraw() { - AddressEntry addressEntry = walletService.getAddressEntry(getTrade().getId()); - log.debug("trade id " + getTrade().getId()); - log.debug("getAddressString " + addressEntry.getAddressString()); - log.debug("funds " + walletService.getBalanceForAddress(addressEntry.getAddress()).subtract(FeePolicy - .TX_FEE).toString()); - // return walletService.getBalanceForAddress(addressEntry.getAddress()).subtract(FeePolicy.TX_FEE); - - // TODO handle overpaid securityDeposit - if (isOfferer()) - return getTrade().getTradeAmount().add(getTrade().getOffer().getSecurityDeposit()); - else - return getTrade().getSecurityDeposit(); + private void removeListenerFromSelectedTrade() { + if (selectedItem != null) { + Trade trade = selectedItem.getTrade(); + if (trade instanceof TakerTrade) + takerProcessState.unbind(); + else + offererProcessState.unbind(); + } } - - private void cleanUpSelectedTrade() { - if (selectedItem != null) { - selectedItem.getTrade().processStateProperty().removeListener(tradeStateChangeListener); - } + public Coin getAmountToWithdraw() { + Trade trade = selectedItem.getTrade(); + Coin amountToWithdraw = trade.getSecurityDeposit(); + if (trade instanceof OffererTrade) + amountToWithdraw = amountToWithdraw.add(trade.getTradeAmount()); + return amountToWithdraw; } } diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesView.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesView.java index 117c371f78..5a02173971 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesView.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesView.java @@ -85,8 +85,8 @@ public class PendingTradesView extends ActivatableViewAndModel selectedIndexChangeListener; private ListChangeListener listChangeListener; private ChangeListener txIdChangeListener; - private ChangeListener offererStateChangeListener; - private ChangeListener takerStateChangeListener; + private ChangeListener offererStateChangeListener; + private ChangeListener takerStateChangeListener; private final Navigation navigation; private ChangeListener focusedPropertyListener; @@ -170,8 +170,8 @@ public class PendingTradesView extends ActivatableViewAndModel implements ViewModel { private static final Logger log = LoggerFactory.getLogger(PendingTradesViewModel.class); - enum State { + enum ViewState { TAKER_SELLER_WAIT_TX_CONF, TAKER_SELLER_WAIT_PAYMENT_STARTED, TAKER_SELLER_CONFIRM_RECEIVE_PAYMENT, @@ -61,16 +60,20 @@ class PendingTradesViewModel extends ActivatableWithDataModel state = new SimpleObjectProperty<>(); final BooleanProperty withdrawalButtonDisable = new SimpleBooleanProperty(true); final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1); + final ObjectProperty viewState = new SimpleObjectProperty<>(); @Inject @@ -80,7 +83,8 @@ class PendingTradesViewModel extends ActivatableWithDataModel updateState(); + this.takerStateListener = (ov) -> updateTakerState(); + this.offererStateListener = (ov) -> updateOffererState(); } @Override @@ -88,8 +92,11 @@ class PendingTradesViewModel extends ActivatableWithDataModel getList() { @@ -218,63 +227,88 @@ class PendingTradesViewModel extends ActivatableWithDataModel { } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); log.error(e.getMessage()); - Popups.openErrorPopup("An exception occurred at reading data from disc.", e.getMessage()); + Throwables.propagate(e); } return null; diff --git a/core/src/main/java/io/bitsquare/trade/OffererTrade.java b/core/src/main/java/io/bitsquare/trade/OffererTrade.java index b0bc486849..4f2d8dd1f4 100644 --- a/core/src/main/java/io/bitsquare/trade/OffererTrade.java +++ b/core/src/main/java/io/bitsquare/trade/OffererTrade.java @@ -64,7 +64,7 @@ public class OffererTrade extends Trade implements Serializable { PAYOUT_PUBLISHED, MESSAGE_SENDING_FAILED, - UNSPECIFIC_FAULT + EXCEPTION } protected OffererProcessState processState; @@ -104,7 +104,7 @@ public class OffererTrade extends Trade implements Serializable { processStateProperty.set(processState); switch (processState) { - case UNSPECIFIC_FAULT: + case EXCEPTION: disposeProtocol(); setLifeCycleState(OffererLifeCycleState.FAILED); break; diff --git a/core/src/main/java/io/bitsquare/trade/Trade.java b/core/src/main/java/io/bitsquare/trade/Trade.java index 1c359fd472..871f4bb88b 100644 --- a/core/src/main/java/io/bitsquare/trade/Trade.java +++ b/core/src/main/java/io/bitsquare/trade/Trade.java @@ -69,8 +69,8 @@ abstract public class Trade implements Serializable { transient protected String errorMessage; transient protected Throwable throwable; - transient protected ObjectProperty tradeAmountProperty = new SimpleObjectProperty<>(tradeAmount); - transient protected ObjectProperty tradeVolumeProperty = new SimpleObjectProperty<>(getTradeVolume()); + transient protected ObjectProperty tradeAmountProperty; + transient protected ObjectProperty tradeVolumeProperty; /////////////////////////////////////////////////////////////////////////////////////////// @@ -80,6 +80,8 @@ abstract public class Trade implements Serializable { public Trade(Offer offer) { this.offer = offer; date = new Date(); + tradeAmountProperty = new SimpleObjectProperty<>(tradeAmount); + tradeVolumeProperty = new SimpleObjectProperty<>(getTradeVolume()); // cannot be set before offer is set } // Serialized object does not create our transient objects diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java index 1f60f32f2c..01ba0ddd49 100644 --- a/core/src/main/java/io/bitsquare/trade/TradeManager.java +++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java @@ -18,9 +18,11 @@ package io.bitsquare.trade; import io.bitsquare.arbitration.ArbitrationRepository; +import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.BlockChainService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.handlers.ErrorMessageHandler; +import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.crypto.EncryptionService; import io.bitsquare.crypto.SignatureService; @@ -47,9 +49,14 @@ import io.bitsquare.trade.protocol.trade.taker.models.TakerAsSellerModel; import io.bitsquare.user.AccountSettings; import io.bitsquare.user.User; +import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.Coin; +import org.bitcoinj.core.InsufficientMoneyException; +import org.bitcoinj.core.Transaction; import org.bitcoinj.utils.Fiat; +import com.google.common.util.concurrent.FutureCallback; + import java.io.File; import java.util.HashMap; @@ -62,6 +69,8 @@ import javax.inject.Named; import javafx.collections.ObservableList; +import org.jetbrains.annotations.NotNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -259,17 +268,49 @@ public class TradeManager { ((TakerTrade) trade).onFiatPaymentReceived(); } - public void onWithdrawAtTradeCompleted(Trade trade) { - if (trade instanceof OffererTrade) - ((OffererTrade) trade).setLifeCycleState(OffererTrade.OffererLifeCycleState.COMPLETED); - else - ((TakerTrade) trade).setLifeCycleState(TakerTrade.TakerLifeCycleState.COMPLETED); + public void requestWithdraw(String toAddress, Trade trade, ResultHandler resultHandler, FaultHandler faultHandler) { + AddressEntry addressEntry = walletService.getAddressEntry(trade.getId()); + String fromAddress = addressEntry.getAddressString(); - pendingTrades.remove(trade); - closedTrades.add(trade); + // TODO handle overpaid securityDeposit + Coin amountToWithdraw = trade.getSecurityDeposit(); + if (trade instanceof OffererTrade) + amountToWithdraw = amountToWithdraw.add(trade.getTradeAmount()); + + FutureCallback callback = new FutureCallback() { + @Override + public void onSuccess(@javax.annotation.Nullable Transaction transaction) { + if (transaction != null) { + log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString()); + if (trade instanceof OffererTrade) + ((OffererTrade) trade).setLifeCycleState(OffererTrade.OffererLifeCycleState.COMPLETED); + else + ((TakerTrade) trade).setLifeCycleState(TakerTrade.TakerLifeCycleState.COMPLETED); + + pendingTrades.remove(trade); + closedTrades.add(trade); + + resultHandler.handleResult(); + } + } + + @Override + public void onFailure(@NotNull Throwable t) { + t.printStackTrace(); + log.error(t.getMessage()); + faultHandler.handleFault("An exception occurred at requestWithdraw (onFailure).", t); + } + }; + try { + walletService.sendFunds(fromAddress, toAddress, amountToWithdraw, callback); + } catch (AddressFormatException | InsufficientMoneyException e) { + e.printStackTrace(); + log.error(e.getMessage()); + faultHandler.handleFault("An exception occurred at requestWithdraw.", e); + } } - + /////////////////////////////////////////////////////////////////////////////////////////// // Called from Offerbook when offer gets removed from DHT /////////////////////////////////////////////////////////////////////////////////////////// @@ -437,5 +478,4 @@ public class TradeManager { log.error(fault.getMessage()); }); } - } \ No newline at end of file diff --git a/core/src/main/java/io/bitsquare/user/User.java b/core/src/main/java/io/bitsquare/user/User.java index 53e47fc4cd..f69c658e65 100644 --- a/core/src/main/java/io/bitsquare/user/User.java +++ b/core/src/main/java/io/bitsquare/user/User.java @@ -19,9 +19,10 @@ package io.bitsquare.user; import io.bitsquare.crypto.EncryptionService; import io.bitsquare.fiat.FiatAccount; -import io.bitsquare.gui.components.Popups; import io.bitsquare.storage.Storage; +import com.google.common.base.Throwables; + import java.io.Serializable; import java.security.KeyPair; @@ -94,7 +95,7 @@ public class User implements Serializable { } catch (NoSuchAlgorithmException e) { e.printStackTrace(); log.error(e.getMessage()); - Popups.openExceptionPopup(e); + Throwables.propagate(e); } } storage.save();