mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-25 07:27:18 +01:00
Fixed state handling in views
This commit is contained in:
parent
9936dca851
commit
ccd2e6b676
10 changed files with 235 additions and 168 deletions
|
@ -78,7 +78,7 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||||
case FAILED:
|
case FAILED:
|
||||||
return "Failed";
|
return "Failed";
|
||||||
case PENDING:
|
case PENDING:
|
||||||
throw new RuntimeException("Wrong state: " + lifeCycleState);
|
throw new RuntimeException("That must not happen. We got a pending state but we are in the closed trades list.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lifeCycleState instanceof OffererTrade.OffererLifeCycleState) {
|
else if (lifeCycleState instanceof OffererTrade.OffererLifeCycleState) {
|
||||||
|
@ -91,11 +91,10 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
|
||||||
return "Failed";
|
return "Failed";
|
||||||
case OPEN_OFFER:
|
case OPEN_OFFER:
|
||||||
case PENDING:
|
case PENDING:
|
||||||
throw new RuntimeException("Wrong state: " + lifeCycleState);
|
throw new RuntimeException("That must not happen. We got a pending state but we are in the closed trades list.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw new RuntimeException("That must not happen. We got no defined state.");
|
||||||
return "Undefined";
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -17,22 +17,19 @@
|
||||||
|
|
||||||
package io.bitsquare.gui.main.portfolio.pending;
|
package io.bitsquare.gui.main.portfolio.pending;
|
||||||
|
|
||||||
import io.bitsquare.btc.AddressEntry;
|
|
||||||
import io.bitsquare.btc.FeePolicy;
|
import io.bitsquare.btc.FeePolicy;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.common.viewfx.model.Activatable;
|
import io.bitsquare.common.viewfx.model.Activatable;
|
||||||
import io.bitsquare.common.viewfx.model.DataModel;
|
import io.bitsquare.common.viewfx.model.DataModel;
|
||||||
|
import io.bitsquare.gui.components.Popups;
|
||||||
import io.bitsquare.offer.Offer;
|
import io.bitsquare.offer.Offer;
|
||||||
|
import io.bitsquare.trade.OffererTrade;
|
||||||
|
import io.bitsquare.trade.TakerTrade;
|
||||||
import io.bitsquare.trade.Trade;
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import org.bitcoinj.core.AddressFormatException;
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
|
||||||
import org.bitcoinj.core.Transaction;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
@ -44,13 +41,10 @@ import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -65,28 +59,27 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
private PendingTradesListItem selectedItem;
|
private PendingTradesListItem selectedItem;
|
||||||
private boolean isOfferer;
|
private boolean isOfferer;
|
||||||
private Trade closedTrade;
|
|
||||||
|
|
||||||
private final ChangeListener<Trade.ProcessState> tradeStateChangeListener;
|
|
||||||
private final ListChangeListener<Trade> tradesListChangeListener;
|
private final ListChangeListener<Trade> tradesListChangeListener;
|
||||||
|
|
||||||
final StringProperty txId = new SimpleStringProperty();
|
final StringProperty txId = new SimpleStringProperty();
|
||||||
final ObjectProperty<Trade.ProcessState> tradeState = new SimpleObjectProperty<>();
|
|
||||||
final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1);
|
final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1);
|
||||||
|
|
||||||
|
final ObjectProperty<TakerTrade.TakerProcessState> takerProcessState = new SimpleObjectProperty<>();
|
||||||
|
final ObjectProperty<OffererTrade.OffererProcessState> offererProcessState = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, User user) {
|
public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, User user) {
|
||||||
this.tradeManager = tradeManager;
|
this.tradeManager = tradeManager;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
|
||||||
tradeStateChangeListener = (ov, oldValue, newValue) -> tradeState.set(newValue);
|
tradesListChangeListener = change -> onListChanged();
|
||||||
tradesListChangeListener = change -> applyList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
applyList();
|
onListChanged();
|
||||||
tradeManager.getPendingTrades().addListener(tradesListChangeListener);
|
tradeManager.getPendingTrades().addListener(tradesListChangeListener);
|
||||||
|
|
||||||
if (list.size() > 0) {
|
if (list.size() > 0) {
|
||||||
|
@ -98,10 +91,10 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
@Override
|
@Override
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
tradeManager.getPendingTrades().removeListener(tradesListChangeListener);
|
tradeManager.getPendingTrades().removeListener(tradesListChangeListener);
|
||||||
cleanUpSelectedTrade();
|
removeListenerFromSelectedTrade();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyList() {
|
private void onListChanged() {
|
||||||
list.clear();
|
list.clear();
|
||||||
list.addAll(tradeManager.getPendingTrades().stream().map(PendingTradesListItem::new).collect(Collectors.toList()));
|
list.addAll(tradeManager.getPendingTrades().stream().map(PendingTradesListItem::new).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
@ -116,7 +109,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
|
|
||||||
void selectTrade(PendingTradesListItem item) {
|
void selectTrade(PendingTradesListItem item) {
|
||||||
// clean up previous selectedItem
|
// clean up previous selectedItem
|
||||||
cleanUpSelectedTrade();
|
removeListenerFromSelectedTrade();
|
||||||
|
|
||||||
selectedItem = item;
|
selectedItem = item;
|
||||||
|
|
||||||
|
@ -124,8 +117,11 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
isOfferer = getTrade().getOffer().getP2PSigPubKey().equals(user.getP2PSigPubKey());
|
isOfferer = getTrade().getOffer().getP2PSigPubKey().equals(user.getP2PSigPubKey());
|
||||||
|
|
||||||
Trade trade = getTrade();
|
Trade trade = getTrade();
|
||||||
trade.processStateProperty().addListener(tradeStateChangeListener);
|
if (trade instanceof TakerTrade)
|
||||||
tradeState.set(trade.processStateProperty().get());
|
takerProcessState.bind(((TakerTrade) trade).processStateProperty());
|
||||||
|
else
|
||||||
|
offererProcessState.bind(((OffererTrade) trade).processStateProperty());
|
||||||
|
|
||||||
log.trace("selectTrade trade.stateProperty().get() " + trade.processStateProperty().get());
|
log.trace("selectTrade trade.stateProperty().get() " + trade.processStateProperty().get());
|
||||||
|
|
||||||
if (trade.getDepositTx() != null)
|
if (trade.getDepositTx() != null)
|
||||||
|
@ -133,7 +129,6 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
txId.set(null);
|
txId.set(null);
|
||||||
tradeState.set(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,34 +141,14 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void withdraw(String toAddress) {
|
void withdraw(String toAddress) {
|
||||||
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() {
|
tradeManager.requestWithdraw(toAddress,
|
||||||
@Override
|
getTrade(),
|
||||||
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
() -> log.debug("requestWithdraw was successful"),
|
||||||
if (transaction != null) {
|
(errorMessage, throwable) -> {
|
||||||
log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
|
log.error(errorMessage);
|
||||||
|
Popups.openExceptionPopup(throwable);
|
||||||
|
});
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Action response = Popups.openConfirmPopup(
|
Action response = Popups.openConfirmPopup(
|
||||||
|
@ -203,7 +178,6 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ObservableList<PendingTradesListItem> getList() {
|
ObservableList<PendingTradesListItem> getList() {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -232,32 +206,36 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||||
return selectedItem.getTrade().getOffer().getCurrencyCode();
|
return selectedItem.getTrade().getOffer().getCurrencyCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Throwable getTradeException() {
|
||||||
|
return getTrade().getThrowable();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getErrorMessage() {
|
||||||
|
return getTrade().getErrorMessage();
|
||||||
|
}
|
||||||
|
|
||||||
public Offer.Direction getDirection(Offer offer) {
|
public Offer.Direction getDirection(Offer offer) {
|
||||||
return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ?
|
return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ?
|
||||||
offer.getDirection() : offer.getMirroredDirection();
|
offer.getDirection() : offer.getMirroredDirection();
|
||||||
}
|
}
|
||||||
|
|
||||||
Coin getAmountToWithdraw() {
|
private void removeListenerFromSelectedTrade() {
|
||||||
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 cleanUpSelectedTrade() {
|
|
||||||
if (selectedItem != null) {
|
if (selectedItem != null) {
|
||||||
selectedItem.getTrade().processStateProperty().removeListener(tradeStateChangeListener);
|
Trade trade = selectedItem.getTrade();
|
||||||
|
if (trade instanceof TakerTrade)
|
||||||
|
takerProcessState.unbind();
|
||||||
|
else
|
||||||
|
offererProcessState.unbind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Coin getAmountToWithdraw() {
|
||||||
|
Trade trade = selectedItem.getTrade();
|
||||||
|
Coin amountToWithdraw = trade.getSecurityDeposit();
|
||||||
|
if (trade instanceof OffererTrade)
|
||||||
|
amountToWithdraw = amountToWithdraw.add(trade.getTradeAmount());
|
||||||
|
return amountToWithdraw;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,8 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
|
||||||
private ChangeListener<Number> selectedIndexChangeListener;
|
private ChangeListener<Number> selectedIndexChangeListener;
|
||||||
private ListChangeListener<PendingTradesListItem> listChangeListener;
|
private ListChangeListener<PendingTradesListItem> listChangeListener;
|
||||||
private ChangeListener<String> txIdChangeListener;
|
private ChangeListener<String> txIdChangeListener;
|
||||||
private ChangeListener<PendingTradesViewModel.State> offererStateChangeListener;
|
private ChangeListener<PendingTradesViewModel.ViewState> offererStateChangeListener;
|
||||||
private ChangeListener<PendingTradesViewModel.State> takerStateChangeListener;
|
private ChangeListener<PendingTradesViewModel.ViewState> takerStateChangeListener;
|
||||||
|
|
||||||
private final Navigation navigation;
|
private final Navigation navigation;
|
||||||
private ChangeListener<Boolean> focusedPropertyListener;
|
private ChangeListener<Boolean> focusedPropertyListener;
|
||||||
|
@ -170,8 +170,8 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
|
||||||
public void doDeactivate() {
|
public void doDeactivate() {
|
||||||
model.getList().removeListener(listChangeListener);
|
model.getList().removeListener(listChangeListener);
|
||||||
model.txId.removeListener(txIdChangeListener);
|
model.txId.removeListener(txIdChangeListener);
|
||||||
model.state.removeListener(offererStateChangeListener);
|
model.viewState.removeListener(offererStateChangeListener);
|
||||||
model.state.removeListener(takerStateChangeListener);
|
model.viewState.removeListener(takerStateChangeListener);
|
||||||
model.selectedIndex.removeListener(selectedIndexChangeListener);
|
model.selectedIndex.removeListener(selectedIndexChangeListener);
|
||||||
table.getSelectionModel().selectedItemProperty().removeListener(selectedItemChangeListener);
|
table.getSelectionModel().selectedItemProperty().removeListener(selectedItemChangeListener);
|
||||||
|
|
||||||
|
@ -258,8 +258,8 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
|
||||||
processBar.setProcessStepItems(items);
|
processBar.setProcessStepItems(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
model.state.addListener(offererStateChangeListener);
|
model.viewState.addListener(offererStateChangeListener);
|
||||||
applyOffererState(model.state.get());
|
applyOffererState(model.viewState.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupScreenForTaker() {
|
private void setupScreenForTaker() {
|
||||||
|
@ -273,18 +273,18 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
|
||||||
processBar.setProcessStepItems(items);
|
processBar.setProcessStepItems(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
model.state.addListener(takerStateChangeListener);
|
model.viewState.addListener(takerStateChangeListener);
|
||||||
applyTakerState(model.state.get());
|
applyTakerState(model.viewState.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyOffererState(PendingTradesViewModel.State state) {
|
private void applyOffererState(PendingTradesViewModel.ViewState viewState) {
|
||||||
setPaymentsControlsVisible(false);
|
setPaymentsControlsVisible(false);
|
||||||
setSummaryControlsVisible(false);
|
setSummaryControlsVisible(false);
|
||||||
log.debug("applyOffererState " + state);
|
log.debug("applyOffererState " + viewState);
|
||||||
processBar.reset();
|
processBar.reset();
|
||||||
|
|
||||||
if (state != null) {
|
if (viewState != null) {
|
||||||
switch (state) {
|
switch (viewState) {
|
||||||
case OFFERER_BUYER_WAIT_TX_CONF:
|
case OFFERER_BUYER_WAIT_TX_CONF:
|
||||||
processBar.setSelectedIndex(0);
|
processBar.setSelectedIndex(0);
|
||||||
|
|
||||||
|
@ -347,21 +347,27 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
|
||||||
|
|
||||||
withdrawAmountTextField.setText(model.getAmountToWithdraw());
|
withdrawAmountTextField.setText(model.getAmountToWithdraw());
|
||||||
break;
|
break;
|
||||||
|
case MESSAGE_SENDING_FAILED:
|
||||||
|
Popups.openWarningPopup("Sending message to trading peer failed.", model.getErrorMessage());
|
||||||
|
break;
|
||||||
|
case EXCEPTION:
|
||||||
|
Popups.openExceptionPopup(model.getTradeException());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyTakerState(PendingTradesViewModel.State state) {
|
private void applyTakerState(PendingTradesViewModel.ViewState viewState) {
|
||||||
confirmPaymentReceiptButton.setVisible(false);
|
confirmPaymentReceiptButton.setVisible(false);
|
||||||
confirmPaymentReceiptButton.setManaged(false);
|
confirmPaymentReceiptButton.setManaged(false);
|
||||||
|
|
||||||
setSummaryControlsVisible(false);
|
setSummaryControlsVisible(false);
|
||||||
|
|
||||||
processBar.reset();
|
processBar.reset();
|
||||||
log.debug("applyTakerState " + state);
|
log.debug("applyTakerState " + viewState);
|
||||||
|
|
||||||
if (state != null) {
|
if (viewState != null) {
|
||||||
switch (state) {
|
switch (viewState) {
|
||||||
case TAKER_SELLER_WAIT_TX_CONF:
|
case TAKER_SELLER_WAIT_TX_CONF:
|
||||||
processBar.setSelectedIndex(0);
|
processBar.setSelectedIndex(0);
|
||||||
|
|
||||||
|
@ -422,6 +428,12 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
|
||||||
|
|
||||||
withdrawAmountTextField.setText(model.getAmountToWithdraw());
|
withdrawAmountTextField.setText(model.getAmountToWithdraw());
|
||||||
break;
|
break;
|
||||||
|
case MESSAGE_SENDING_FAILED:
|
||||||
|
Popups.openWarningPopup("Sending message to trading peer failed.", model.getErrorMessage());
|
||||||
|
break;
|
||||||
|
case EXCEPTION:
|
||||||
|
Popups.openExceptionPopup(model.getTradeException());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import io.bitsquare.gui.util.validation.BtcAddressValidator;
|
||||||
import io.bitsquare.locale.BSResources;
|
import io.bitsquare.locale.BSResources;
|
||||||
import io.bitsquare.trade.OffererTrade;
|
import io.bitsquare.trade.OffererTrade;
|
||||||
import io.bitsquare.trade.TakerTrade;
|
import io.bitsquare.trade.TakerTrade;
|
||||||
import io.bitsquare.trade.Trade;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.utils.Fiat;
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
@ -51,7 +50,7 @@ import org.slf4j.LoggerFactory;
|
||||||
class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataModel> implements ViewModel {
|
class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataModel> implements ViewModel {
|
||||||
private static final Logger log = LoggerFactory.getLogger(PendingTradesViewModel.class);
|
private static final Logger log = LoggerFactory.getLogger(PendingTradesViewModel.class);
|
||||||
|
|
||||||
enum State {
|
enum ViewState {
|
||||||
TAKER_SELLER_WAIT_TX_CONF,
|
TAKER_SELLER_WAIT_TX_CONF,
|
||||||
TAKER_SELLER_WAIT_PAYMENT_STARTED,
|
TAKER_SELLER_WAIT_PAYMENT_STARTED,
|
||||||
TAKER_SELLER_CONFIRM_RECEIVE_PAYMENT,
|
TAKER_SELLER_CONFIRM_RECEIVE_PAYMENT,
|
||||||
|
@ -61,16 +60,20 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
|
||||||
OFFERER_BUYER_START_PAYMENT,
|
OFFERER_BUYER_START_PAYMENT,
|
||||||
OFFERER_BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED,
|
OFFERER_BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED,
|
||||||
OFFERER_BUYER_COMPLETED,
|
OFFERER_BUYER_COMPLETED,
|
||||||
|
|
||||||
|
MESSAGE_SENDING_FAILED,
|
||||||
|
EXCEPTION
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
private final InvalidationListener stateChangeListener;
|
private final InvalidationListener takerStateListener;
|
||||||
|
private final InvalidationListener offererStateListener;
|
||||||
private final BtcAddressValidator btcAddressValidator;
|
private final BtcAddressValidator btcAddressValidator;
|
||||||
|
|
||||||
final StringProperty txId = new SimpleStringProperty();
|
final StringProperty txId = new SimpleStringProperty();
|
||||||
final ObjectProperty<State> state = new SimpleObjectProperty<>();
|
|
||||||
final BooleanProperty withdrawalButtonDisable = new SimpleBooleanProperty(true);
|
final BooleanProperty withdrawalButtonDisable = new SimpleBooleanProperty(true);
|
||||||
final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1);
|
final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1);
|
||||||
|
final ObjectProperty<ViewState> viewState = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -80,7 +83,8 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
|
||||||
|
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
this.btcAddressValidator = btcAddressValidator;
|
this.btcAddressValidator = btcAddressValidator;
|
||||||
this.stateChangeListener = (ov) -> updateState();
|
this.takerStateListener = (ov) -> updateTakerState();
|
||||||
|
this.offererStateListener = (ov) -> updateOffererState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -88,8 +92,11 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
|
||||||
txId.bind(dataModel.txId);
|
txId.bind(dataModel.txId);
|
||||||
selectedIndex.bind(dataModel.selectedIndex);
|
selectedIndex.bind(dataModel.selectedIndex);
|
||||||
|
|
||||||
dataModel.tradeState.addListener(stateChangeListener);
|
dataModel.takerProcessState.addListener(takerStateListener);
|
||||||
updateState();
|
dataModel.offererProcessState.addListener(offererStateListener);
|
||||||
|
|
||||||
|
updateTakerState();
|
||||||
|
updateOffererState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -97,13 +104,15 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
|
||||||
txId.unbind();
|
txId.unbind();
|
||||||
selectedIndex.unbind();
|
selectedIndex.unbind();
|
||||||
|
|
||||||
dataModel.tradeState.removeListener(stateChangeListener);
|
dataModel.takerProcessState.removeListener(takerStateListener);
|
||||||
|
dataModel.offererProcessState.removeListener(offererStateListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void selectTrade(PendingTradesListItem item) {
|
void selectTrade(PendingTradesListItem item) {
|
||||||
dataModel.selectTrade(item);
|
dataModel.selectTrade(item);
|
||||||
updateState();
|
/* updateTakerState();
|
||||||
|
updateOffererState();*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void fiatPaymentStarted() {
|
void fiatPaymentStarted() {
|
||||||
|
@ -123,7 +132,7 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
|
||||||
}
|
}
|
||||||
|
|
||||||
String getAmountToWithdraw() {
|
String getAmountToWithdraw() {
|
||||||
return formatter.formatCoinWithCode(dataModel.getAmountToWithdraw()); //.subtract(FeePolicy.TX_FEE));
|
return formatter.formatCoinWithCode(dataModel.getAmountToWithdraw());
|
||||||
}
|
}
|
||||||
|
|
||||||
ObservableList<PendingTradesListItem> getList() {
|
ObservableList<PendingTradesListItem> getList() {
|
||||||
|
@ -218,63 +227,88 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
|
||||||
return btcAddressValidator;
|
return btcAddressValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Throwable getTradeException() {
|
||||||
|
return dataModel.getTradeException();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateState() {
|
String getErrorMessage() {
|
||||||
Trade.ProcessState processState = dataModel.tradeState.get();
|
return dataModel.getErrorMessage();
|
||||||
log.trace("tradeState " + processState);
|
}
|
||||||
|
|
||||||
|
private void updateTakerState() {
|
||||||
|
TakerTrade.TakerProcessState processState = dataModel.takerProcessState.get();
|
||||||
|
log.debug("updateTakerState " + processState);
|
||||||
if (processState != null) {
|
if (processState != null) {
|
||||||
if (processState instanceof TakerTrade.TakerProcessState) {
|
switch (processState) {
|
||||||
switch ((TakerTrade.TakerProcessState) processState) {
|
case TAKE_OFFER_FEE_TX_CREATED:
|
||||||
|
break;
|
||||||
|
case TAKE_OFFER_FEE_PUBLISHED:
|
||||||
|
break;
|
||||||
|
case TAKE_OFFER_FEE_PUBLISH_FAILED:
|
||||||
|
break;
|
||||||
|
|
||||||
case DEPOSIT_PUBLISHED:
|
case DEPOSIT_PUBLISHED:
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_WAIT_TX_CONF : State.TAKER_SELLER_WAIT_TX_CONF);
|
viewState.set(ViewState.TAKER_SELLER_WAIT_TX_CONF);
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_CONFIRMED:
|
case DEPOSIT_CONFIRMED:
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_START_PAYMENT :
|
viewState.set(ViewState.TAKER_SELLER_WAIT_PAYMENT_STARTED);
|
||||||
State.TAKER_SELLER_WAIT_PAYMENT_STARTED);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FIAT_PAYMENT_STARTED:
|
case FIAT_PAYMENT_STARTED:
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED :
|
viewState.set(ViewState.TAKER_SELLER_CONFIRM_RECEIVE_PAYMENT);
|
||||||
State.TAKER_SELLER_CONFIRM_RECEIVE_PAYMENT);
|
break;
|
||||||
|
|
||||||
|
case FIAT_PAYMENT_RECEIVED:
|
||||||
|
viewState.set(ViewState.TAKER_SELLER_COMPLETED);
|
||||||
break;
|
break;
|
||||||
case PAYOUT_PUBLISHED:
|
case PAYOUT_PUBLISHED:
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_COMPLETED : State.TAKER_SELLER_COMPLETED);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MESSAGE_SENDING_FAILED:
|
case MESSAGE_SENDING_FAILED:
|
||||||
// TODO error states not implemented yet
|
viewState.set(ViewState.MESSAGE_SENDING_FAILED);
|
||||||
break;
|
break;
|
||||||
|
case EXCEPTION:
|
||||||
|
viewState.set(ViewState.EXCEPTION);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.warn("unhandled state " + processState);
|
log.warn("unhandled processState " + processState);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (processState instanceof OffererTrade.OffererProcessState) {
|
|
||||||
switch ((OffererTrade.OffererProcessState) processState) {
|
|
||||||
case DEPOSIT_PUBLISHED:
|
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_WAIT_TX_CONF : State.TAKER_SELLER_WAIT_TX_CONF);
|
|
||||||
break;
|
|
||||||
case DEPOSIT_CONFIRMED:
|
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_START_PAYMENT :
|
|
||||||
State.TAKER_SELLER_WAIT_PAYMENT_STARTED);
|
|
||||||
break;
|
|
||||||
case FIAT_PAYMENT_STARTED:
|
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED :
|
|
||||||
State.TAKER_SELLER_CONFIRM_RECEIVE_PAYMENT);
|
|
||||||
break;
|
|
||||||
case PAYOUT_PUBLISHED:
|
|
||||||
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_COMPLETED : State.TAKER_SELLER_COMPLETED);
|
|
||||||
break;
|
|
||||||
case MESSAGE_SENDING_FAILED:
|
|
||||||
// TODO error states not implemented yet
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
log.warn("unhandled state " + processState);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
state.set(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateOffererState() {
|
||||||
|
OffererTrade.OffererProcessState processState = dataModel.offererProcessState.get();
|
||||||
|
log.debug("updateOffererState " + processState);
|
||||||
|
if (processState != null) {
|
||||||
|
switch (processState) {
|
||||||
|
case DEPOSIT_PUBLISHED:
|
||||||
|
viewState.set(ViewState.OFFERER_BUYER_WAIT_TX_CONF);
|
||||||
|
break;
|
||||||
|
case DEPOSIT_CONFIRMED:
|
||||||
|
viewState.set(ViewState.OFFERER_BUYER_START_PAYMENT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FIAT_PAYMENT_STARTED:
|
||||||
|
viewState.set(ViewState.OFFERER_BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAYOUT_PUBLISHED:
|
||||||
|
viewState.set(ViewState.OFFERER_BUYER_COMPLETED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MESSAGE_SENDING_FAILED:
|
||||||
|
viewState.set(ViewState.MESSAGE_SENDING_FAILED);
|
||||||
|
break;
|
||||||
|
case EXCEPTION:
|
||||||
|
viewState.set(ViewState.EXCEPTION);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.warn("unhandled viewState " + processState);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
package io.bitsquare.storage;
|
package io.bitsquare.storage;
|
||||||
|
|
||||||
|
|
||||||
import io.bitsquare.gui.components.Popups;
|
import io.bitsquare.gui.components.Popups;
|
||||||
|
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
package io.bitsquare.storage;
|
package io.bitsquare.storage;
|
||||||
|
|
||||||
import io.bitsquare.gui.components.Popups;
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -132,7 +132,7 @@ public class Storage<T extends Serializable> {
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
Popups.openErrorPopup("An exception occurred at reading data from disc.", e.getMessage());
|
Throwables.propagate(e);
|
||||||
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class OffererTrade extends Trade implements Serializable {
|
||||||
PAYOUT_PUBLISHED,
|
PAYOUT_PUBLISHED,
|
||||||
|
|
||||||
MESSAGE_SENDING_FAILED,
|
MESSAGE_SENDING_FAILED,
|
||||||
UNSPECIFIC_FAULT
|
EXCEPTION
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OffererProcessState processState;
|
protected OffererProcessState processState;
|
||||||
|
@ -104,7 +104,7 @@ public class OffererTrade extends Trade implements Serializable {
|
||||||
processStateProperty.set(processState);
|
processStateProperty.set(processState);
|
||||||
|
|
||||||
switch (processState) {
|
switch (processState) {
|
||||||
case UNSPECIFIC_FAULT:
|
case EXCEPTION:
|
||||||
disposeProtocol();
|
disposeProtocol();
|
||||||
setLifeCycleState(OffererLifeCycleState.FAILED);
|
setLifeCycleState(OffererLifeCycleState.FAILED);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -69,8 +69,8 @@ abstract public class Trade implements Serializable {
|
||||||
|
|
||||||
transient protected String errorMessage;
|
transient protected String errorMessage;
|
||||||
transient protected Throwable throwable;
|
transient protected Throwable throwable;
|
||||||
transient protected ObjectProperty<Coin> tradeAmountProperty = new SimpleObjectProperty<>(tradeAmount);
|
transient protected ObjectProperty<Coin> tradeAmountProperty;
|
||||||
transient protected ObjectProperty<Fiat> tradeVolumeProperty = new SimpleObjectProperty<>(getTradeVolume());
|
transient protected ObjectProperty<Fiat> tradeVolumeProperty;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -80,6 +80,8 @@ abstract public class Trade implements Serializable {
|
||||||
public Trade(Offer offer) {
|
public Trade(Offer offer) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
date = new Date();
|
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
|
// Serialized object does not create our transient objects
|
||||||
|
|
|
@ -18,9 +18,11 @@
|
||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.arbitration.ArbitrationRepository;
|
import io.bitsquare.arbitration.ArbitrationRepository;
|
||||||
|
import io.bitsquare.btc.AddressEntry;
|
||||||
import io.bitsquare.btc.BlockChainService;
|
import io.bitsquare.btc.BlockChainService;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||||
|
import io.bitsquare.common.handlers.FaultHandler;
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
import io.bitsquare.common.handlers.ResultHandler;
|
||||||
import io.bitsquare.crypto.EncryptionService;
|
import io.bitsquare.crypto.EncryptionService;
|
||||||
import io.bitsquare.crypto.SignatureService;
|
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.AccountSettings;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.AddressFormatException;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.bitcoinj.core.InsufficientMoneyException;
|
||||||
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.utils.Fiat;
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -62,6 +69,8 @@ import javax.inject.Named;
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -259,7 +268,20 @@ public class TradeManager {
|
||||||
((TakerTrade) trade).onFiatPaymentReceived();
|
((TakerTrade) trade).onFiatPaymentReceived();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onWithdrawAtTradeCompleted(Trade trade) {
|
public void requestWithdraw(String toAddress, Trade trade, ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||||
|
AddressEntry addressEntry = walletService.getAddressEntry(trade.getId());
|
||||||
|
String fromAddress = addressEntry.getAddressString();
|
||||||
|
|
||||||
|
// TODO handle overpaid securityDeposit
|
||||||
|
Coin amountToWithdraw = trade.getSecurityDeposit();
|
||||||
|
if (trade instanceof OffererTrade)
|
||||||
|
amountToWithdraw = amountToWithdraw.add(trade.getTradeAmount());
|
||||||
|
|
||||||
|
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
||||||
|
if (transaction != null) {
|
||||||
|
log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
|
||||||
if (trade instanceof OffererTrade)
|
if (trade instanceof OffererTrade)
|
||||||
((OffererTrade) trade).setLifeCycleState(OffererTrade.OffererLifeCycleState.COMPLETED);
|
((OffererTrade) trade).setLifeCycleState(OffererTrade.OffererLifeCycleState.COMPLETED);
|
||||||
else
|
else
|
||||||
|
@ -267,6 +289,25 @@ public class TradeManager {
|
||||||
|
|
||||||
pendingTrades.remove(trade);
|
pendingTrades.remove(trade);
|
||||||
closedTrades.add(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -437,5 +478,4 @@ public class TradeManager {
|
||||||
log.error(fault.getMessage());
|
log.error(fault.getMessage());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,9 +19,10 @@ package io.bitsquare.user;
|
||||||
|
|
||||||
import io.bitsquare.crypto.EncryptionService;
|
import io.bitsquare.crypto.EncryptionService;
|
||||||
import io.bitsquare.fiat.FiatAccount;
|
import io.bitsquare.fiat.FiatAccount;
|
||||||
import io.bitsquare.gui.components.Popups;
|
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
|
@ -94,7 +95,7 @@ public class User implements Serializable {
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
Popups.openExceptionPopup(e);
|
Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storage.save();
|
storage.save();
|
||||||
|
|
Loading…
Add table
Reference in a new issue