Fix pending trades view update bugs

This commit is contained in:
Manfred Karrer 2015-03-19 16:32:12 +01:00
parent 0ac6b39270
commit b416408f89
4 changed files with 89 additions and 52 deletions

View file

@ -21,13 +21,13 @@ 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.btc.listeners.TxConfidenceListener; import io.bitsquare.btc.listeners.TxConfidenceListener;
import io.bitsquare.common.viewfx.model.Activatable;
import io.bitsquare.common.viewfx.model.DataModel;
import io.bitsquare.offer.Direction; import io.bitsquare.offer.Direction;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
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 io.bitsquare.common.viewfx.model.Activatable;
import io.bitsquare.common.viewfx.model.DataModel;
import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -39,9 +39,9 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Optional; import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
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;
@ -67,14 +67,14 @@ class PendingTradesDataModel implements Activatable, DataModel {
private PendingTradesListItem selectedItem; private PendingTradesListItem selectedItem;
private boolean isOfferer; private boolean isOfferer;
private Trade closedTrade; private Trade closedTrade;
private TxConfidenceListener txConfidenceListener;
private final ChangeListener<Trade.State> stateChangeListener; private TxConfidenceListener txConfidenceListener;
private final ChangeListener<Trade.State> tradeStateChangeListener;
private final MapChangeListener<String, Trade> mapChangeListener; private final MapChangeListener<String, Trade> mapChangeListener;
final StringProperty txId = new SimpleStringProperty(); final StringProperty txId = new SimpleStringProperty();
final ObjectProperty<Trade.State> tradeState = new SimpleObjectProperty<>(); final ObjectProperty<Trade.State> tradeState = new SimpleObjectProperty<>();
final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1);
@Inject @Inject
public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, User user) { public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, User user) {
@ -82,13 +82,23 @@ class PendingTradesDataModel implements Activatable, DataModel {
this.walletService = walletService; this.walletService = walletService;
this.user = user; this.user = user;
this.stateChangeListener = (ov, oldValue, newValue) -> tradeState.set(newValue); tradeStateChangeListener = (ov, oldValue, newValue) -> tradeState.set(newValue);
this.mapChangeListener = change -> { mapChangeListener = change -> {
if (change.wasAdded()) if (change.wasAdded()) {
list.add(new PendingTradesListItem(change.getValueAdded())); list.add(new PendingTradesListItem(change.getValueAdded()));
else if (change.wasRemoved()) if (list.size() == 1) {
selectTrade(list.get(0));
selectedIndex.set(0);
}
}
else if (change.wasRemoved()) {
closedTrade = change.getValueRemoved(); closedTrade = change.getValueRemoved();
if (list.size() == 0) {
selectTrade(null);
selectedIndex.set(-1);
}
}
sortList(); sortList();
}; };
@ -98,27 +108,33 @@ class PendingTradesDataModel implements Activatable, DataModel {
public void activate() { public void activate() {
list.clear(); list.clear();
// transform trades to list of PendingTradesListItems and keep it updated // transform trades to list of PendingTradesListItems and keep it updated
tradeManager.getPendingTrades().values().stream() tradeManager.getPendingTrades().values().stream().forEach(e -> list.add(new PendingTradesListItem(e)));
.forEach(e -> list.add(new PendingTradesListItem(e)));
tradeManager.getPendingTrades().addListener(mapChangeListener); tradeManager.getPendingTrades().addListener(mapChangeListener);
// we sort by date, earliest first // we sort by date, earliest first
sortList(); sortList();
// select either currentPendingTrade or first in the list // select either currentPendingTrade or first in the list
Optional<PendingTradesListItem> currentTradeItemOptional = list.stream() if (tradeManager.getCurrentPendingTrade() != null) {
.filter((e) -> tradeManager.getCurrentPendingTrade() != null && for (int i = 0; i < list.size(); i++) {
tradeManager.getCurrentPendingTrade().getId().equals(e.getTrade().getId())) PendingTradesListItem item = list.get(i);
.findFirst(); if (tradeManager.getCurrentPendingTrade().getId().equals(item.getTrade().getId())) {
if (currentTradeItemOptional.isPresent()) selectedIndex.set(i);
selectTrade(currentTradeItemOptional.get()); selectTrade(item);
else if (list.size() > 0) break;
}
}
}
else if (list.size() > 0) {
selectTrade(list.get(0)); selectTrade(list.get(0));
selectedIndex.set(0);
}
} }
@Override @Override
public void deactivate() { public void deactivate() {
tradeManager.getPendingTrades().removeListener(mapChangeListener); tradeManager.getPendingTrades().removeListener(mapChangeListener);
cleanUpSelectedTrade(); cleanUpSelectedTrade();
} }
@ -133,7 +149,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
isOfferer = getTrade().getOffer().getMessagePublicKey().equals(user.getMessagePubKey()); isOfferer = getTrade().getOffer().getMessagePublicKey().equals(user.getMessagePubKey());
Trade trade = getTrade(); Trade trade = getTrade();
trade.stateProperty().addListener(stateChangeListener); trade.stateProperty().addListener(tradeStateChangeListener);
tradeState.set(trade.stateProperty().get()); tradeState.set(trade.stateProperty().get());
log.trace("selectTrade trade.stateProperty().get() " + trade.stateProperty().get()); log.trace("selectTrade trade.stateProperty().get() " + trade.stateProperty().get());
@ -287,8 +303,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
private void cleanUpSelectedTrade() { private void cleanUpSelectedTrade() {
if (selectedItem != null) { if (selectedItem != null) {
Trade trade = getTrade(); selectedItem.getTrade().stateProperty().removeListener(tradeStateChangeListener);
trade.stateProperty().removeListener(stateChangeListener);
} }
if (txConfidenceListener != null) if (txConfidenceListener != null)

View file

@ -82,13 +82,15 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
@FXML TableColumn<PendingTradesListItem, Date> dateColumn; @FXML TableColumn<PendingTradesListItem, Date> dateColumn;
@FXML TableColumn<PendingTradesListItem, Coin> tradeAmountColumn; @FXML TableColumn<PendingTradesListItem, Coin> tradeAmountColumn;
private ChangeListener<PendingTradesListItem> selectedItemChangeListener; 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.State> offererStateChangeListener;
private ChangeListener<PendingTradesViewModel.State> takerStateChangeListener; private ChangeListener<PendingTradesViewModel.State> takerStateChangeListener;
private final Navigation navigation; private final Navigation navigation;
private ChangeListener<Boolean> focusedPropertyListener;
private ChangeListener<PendingTradesListItem> selectedItemChangeListener;
@Inject @Inject
public PendingTradesView(PendingTradesViewModel model, Navigation navigation) { public PendingTradesView(PendingTradesViewModel model, Navigation navigation) {
@ -112,60 +114,68 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setPlaceholder(new Label("No pending trades available")); table.setPlaceholder(new Label("No pending trades available"));
txIdChangeListener = (ov, oldValue, newValue) -> txIdChangeListener = (ov, oldValue, newValue) -> txIdTextField.setup(model.getWalletService(), newValue);
txIdTextField.setup(model.getWalletService(), newValue);
selectedIndexChangeListener = (ov, oldValue, newValue) -> {
if ((Integer) newValue > -1)
table.getSelectionModel().select((Integer) newValue);
selectedItemChangeListener = (obsValue, oldValue, newValue) -> {
if (oldValue != null && newValue != null) {
model.selectTrade(newValue);
updateScreen(); updateScreen();
}
}; };
listChangeListener = change -> { listChangeListener = change -> {
change.next(); change.next();
if ((change.wasAdded() && change.getList().size() == 1) || if ((change.wasAdded() && change.getList().size() == 1) ||
(change.wasRemoved() && change.getList().size() == 0)) (change.wasRemoved() && change.getList().size() == 0))
updateScreen(); updateScreen();
}; };
offererStateChangeListener = (ov, oldValue, newValue) -> applyOffererState(newValue); offererStateChangeListener = (ov, oldValue, newValue) -> applyOffererState(newValue);
takerStateChangeListener = (ov, oldValue, newValue) -> applyTakerState(newValue); takerStateChangeListener = (ov, oldValue, newValue) -> applyTakerState(newValue);
focusedPropertyListener = (ov, oldValue, newValue) -> {
if (oldValue && !newValue)
model.withdrawAddressFocusOut(withdrawAddressTextField.getText());
};
selectedItemChangeListener = (ov, oldValue, newValue) -> {
model.selectTrade(newValue);
updateScreen();
};
withdrawAddressTextField.setValidator(model.getBtcAddressValidator()); withdrawAddressTextField.setValidator(model.getBtcAddressValidator());
withdrawButton.disableProperty().bind(model.withdrawalButtonDisable);
} }
@Override @Override
public void doActivate() { public void doActivate() {
table.setItems(model.getList()); table.setItems(model.getList());
table.getSelectionModel().selectedItemProperty().addListener(selectedItemChangeListener);
model.getList().addListener(listChangeListener); model.getList().addListener(listChangeListener);
model.txId.addListener(txIdChangeListener); model.txId.addListener(txIdChangeListener);
model.selectedIndex.addListener(selectedIndexChangeListener);
withdrawAddressTextField.focusedProperty().addListener(focusedPropertyListener);
txIdTextField.setup(model.getWalletService(), model.txId.get());
table.getSelectionModel().select(model.getSelectedItem());
table.getSelectionModel().selectedItemProperty().addListener(selectedItemChangeListener);
// TODO Set focus to row does not work yet... // TODO Set focus to row does not work yet...
/* table.requestFocus(); Platform.runLater(() -> table.requestFocus());
table.getFocusModel().focus( table.getSelectionModel().getSelectedIndex());*/ table.getFocusModel().focus(model.selectedIndex.get());
txIdTextField.setup(model.getWalletService(), model.txId.get());
selectedIndexChangeListener.changed(null, null, model.selectedIndex.get());
withdrawAddressTextField.focusedProperty().addListener((ov, oldValue, newValue) -> { withdrawButton.disableProperty().bind(model.withdrawalButtonDisable);
if (oldValue && !newValue)
model.withdrawAddressFocusOut(withdrawAddressTextField.getText());
});
updateScreen(); updateScreen();
} }
@Override @Override
public void doDeactivate() { public void doDeactivate() {
table.getSelectionModel().selectedItemProperty().removeListener(selectedItemChangeListener);
model.getList().removeListener(listChangeListener); model.getList().removeListener(listChangeListener);
model.txId.removeListener(txIdChangeListener); model.txId.removeListener(txIdChangeListener);
model.state.removeListener(offererStateChangeListener); model.state.removeListener(offererStateChangeListener);
model.state.removeListener(takerStateChangeListener); model.state.removeListener(takerStateChangeListener);
model.selectedIndex.removeListener(selectedIndexChangeListener);
table.getSelectionModel().selectedItemProperty().removeListener(selectedItemChangeListener);
withdrawButton.disableProperty().unbind();
} }
@FXML @FXML
@ -450,7 +460,8 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
fiatAmountTextField.setManaged(visible); fiatAmountTextField.setManaged(visible);
holderNameTextField.setManaged(visible); holderNameTextField.setManaged(visible);
Platform.runLater(() -> scrollPane.setVvalue(visible ? scrollPane.getVmax() : 0)); if (visible)
Platform.runLater(() -> scrollPane.setVvalue(scrollPane.getVmax()));
} }
private void setSummaryControlsVisible(boolean visible) { private void setSummaryControlsVisible(boolean visible) {
@ -491,11 +502,14 @@ public class PendingTradesView extends ActivatableViewAndModel<AnchorPane, Pendi
withdrawButton.setManaged(visible); withdrawButton.setManaged(visible);
if (visible) if (visible)
Platform.runLater(() -> {
withdrawAddressTextField.requestFocus(); withdrawAddressTextField.requestFocus();
scrollPane.setVvalue(scrollPane.getVmax());
Platform.runLater(() -> scrollPane.setVvalue(visible ? scrollPane.getVmax() : 0)); });
} }
// CellFactories
private void setTradeIdColumnCellFactory() { private void setTradeIdColumnCellFactory() {
idColumn.setCellFactory( idColumn.setCellFactory(
new Callback<TableColumn<PendingTradesListItem, String>, TableCell<PendingTradesListItem, String>>() { new Callback<TableColumn<PendingTradesListItem, String>, TableCell<PendingTradesListItem, String>>() {

View file

@ -34,8 +34,10 @@ import java.util.Date;
import javafx.beans.InvalidationListener; import javafx.beans.InvalidationListener;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
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;
@ -66,6 +68,7 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
final StringProperty txId = new SimpleStringProperty(); final StringProperty txId = new SimpleStringProperty();
final ObjectProperty<State> state = new SimpleObjectProperty<>(); final ObjectProperty<State> state = new SimpleObjectProperty<>();
final BooleanProperty withdrawalButtonDisable = new SimpleBooleanProperty(true); final BooleanProperty withdrawalButtonDisable = new SimpleBooleanProperty(true);
final IntegerProperty selectedIndex = new SimpleIntegerProperty(-1);
@Inject @Inject
@ -81,6 +84,7 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
@Override @Override
public void doActivate() { public void doActivate() {
txId.bind(dataModel.txId); txId.bind(dataModel.txId);
selectedIndex.bind(dataModel.selectedIndex);
dataModel.tradeState.addListener(stateChangeListener); dataModel.tradeState.addListener(stateChangeListener);
updateState(); updateState();
@ -89,6 +93,7 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
@Override @Override
public void doDeactivate() { public void doDeactivate() {
txId.unbind(); txId.unbind();
selectedIndex.unbind();
dataModel.tradeState.removeListener(stateChangeListener); dataModel.tradeState.removeListener(stateChangeListener);
} }

View file

@ -48,6 +48,8 @@ import io.bitsquare.user.User;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat; import org.bitcoinj.utils.Fiat;
import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -102,17 +104,17 @@ public class TradeManager {
this.signatureService = signatureService; this.signatureService = signatureService;
this.offerBookService = offerBookService; this.offerBookService = offerBookService;
Object openOffersObject = persistence.read(this, "openOffers"); Serializable openOffersObject = persistence.read(this, "openOffers");
if (openOffersObject instanceof Map<?, ?>) { if (openOffersObject instanceof Map<?, ?>) {
openOffers.putAll((Map<String, Offer>) openOffersObject); openOffers.putAll((Map<String, Offer>) openOffersObject);
} }
Object pendingTradesObject = persistence.read(this, "pendingTrades"); Serializable pendingTradesObject = persistence.read(this, "pendingTrades");
if (pendingTradesObject instanceof Map<?, ?>) { if (pendingTradesObject instanceof Map<?, ?>) {
pendingTrades.putAll((Map<String, Trade>) pendingTradesObject); pendingTrades.putAll((Map<String, Trade>) pendingTradesObject);
} }
Object closedTradesObject = persistence.read(this, "closedTrades"); Serializable closedTradesObject = persistence.read(this, "closedTrades");
if (closedTradesObject instanceof Map<?, ?>) { if (closedTradesObject instanceof Map<?, ?>) {
closedTrades.putAll((Map<String, Trade>) closedTradesObject); closedTrades.putAll((Map<String, Trade>) closedTradesObject);
} }
@ -120,7 +122,8 @@ public class TradeManager {
tradeMessageService.addMessageHandler(this::handleMessage); tradeMessageService.addMessageHandler(this::handleMessage);
} }
// When all services are initialized we create the protocols for our open offers (which will listen for take offer requests) // When all services are initialized we create the protocols for our open offers and persisted not completed pendingTrades
// BuyerAcceptsOfferProtocol listens for take offer requests, so we need to instantiate it early.
public void onAllServicesInitialized() { public void onAllServicesInitialized() {
for (Map.Entry<String, Offer> entry : openOffers.entrySet()) { for (Map.Entry<String, Offer> entry : openOffers.entrySet()) {
createBuyerAcceptsOfferProtocol(entry.getValue()); createBuyerAcceptsOfferProtocol(entry.getValue());