Refactored state handling

This commit is contained in:
Manfred Karrer 2015-03-22 00:41:01 +01:00
parent f68030e488
commit 79931b96aa
14 changed files with 184 additions and 146 deletions

View file

@ -87,6 +87,7 @@ lower gradient color on tab: dddddd
#nav-alert-label { #nav-alert-label {
-fx-font-weight: bold; -fx-font-weight: bold;
-fx-alignment: center;
-fx-font-size: 11; -fx-font-size: 11;
-fx-text-fill: white; -fx-text-fill: white;
} }

View file

@ -61,7 +61,6 @@ 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.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.util.StringConverter; import javafx.util.StringConverter;
@ -103,7 +102,6 @@ class MainViewModel implements ViewModel {
private final User user; private final User user;
private final WalletService walletService; private final WalletService walletService;
private final ClientNode clientNode; private final ClientNode clientNode;
private MessageService messageService;
private ArbitratorService arbitratorService; private ArbitratorService arbitratorService;
private final TradeManager tradeManager; private final TradeManager tradeManager;
private UpdateProcess updateProcess; private UpdateProcess updateProcess;
@ -119,7 +117,6 @@ class MainViewModel implements ViewModel {
this.user = user; this.user = user;
this.walletService = walletService; this.walletService = walletService;
this.clientNode = clientNode; this.clientNode = clientNode;
this.messageService = messageService;
this.arbitratorService = arbitratorService; this.arbitratorService = arbitratorService;
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.updateProcess = updateProcess; this.updateProcess = updateProcess;
@ -210,8 +207,7 @@ class MainViewModel implements ViewModel {
private void onAllServicesInitialized() { private void onAllServicesInitialized() {
log.trace("backend completed"); log.trace("backend completed");
tradeManager.getPendingTrades().addListener( tradeManager.getPendingTrades().addListener((ListChangeListener<Trade>) change -> updateNumPendingTrades());
(MapChangeListener<String, Trade>) change -> updateNumPendingTrades());
updateNumPendingTrades(); updateNumPendingTrades();
showAppScreen.set(true); showAppScreen.set(true);
@ -333,9 +329,12 @@ class MainViewModel implements ViewModel {
} }
private void updateNumPendingTrades() { private void updateNumPendingTrades() {
int numPendingTrades = tradeManager.getPendingTrades().size(); long numPendingTrades = tradeManager.getPendingTrades().size();
if (numPendingTrades > 0) if (numPendingTrades > 0)
numPendingTradesAsString.set(String.valueOf(numPendingTrades)); numPendingTradesAsString.set(String.valueOf(numPendingTrades));
if (numPendingTrades > 9)
numPendingTradesAsString.set("*");
showPendingTradesNotification.set(numPendingTrades > 0); showPendingTradesNotification.set(numPendingTrades > 0);
} }

View file

@ -78,10 +78,12 @@ public class PortfolioView extends ActivatableViewAndModel<TabPane, Activatable>
root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener); root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener);
navigation.addListener(navigationListener); navigation.addListener(navigationListener);
if (tradeManager.getPendingTrades().size() == 0) navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
/* if (tradeManager.getPendingTrades().size() == 0)
navigation.navigateTo(MainView.class, PortfolioView.class, OffersView.class); navigation.navigateTo(MainView.class, PortfolioView.class, OffersView.class);
else else
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class); navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);*/
} }
@Override @Override

View file

@ -27,8 +27,10 @@ import io.bitsquare.user.User;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.stream.Collectors;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
class ClosedTradesDataModel implements Activatable, DataModel { class ClosedTradesDataModel implements Activatable, DataModel {
@ -37,36 +39,25 @@ class ClosedTradesDataModel implements Activatable, DataModel {
private final User user; private final User user;
private final ObservableList<ClosedTradesListItem> list = FXCollections.observableArrayList(); private final ObservableList<ClosedTradesListItem> list = FXCollections.observableArrayList();
private final MapChangeListener<String, Trade> mapChangeListener; private final ListChangeListener<Trade> tradesListChangeListener;
@Inject @Inject
public ClosedTradesDataModel(TradeManager tradeManager, User user) { public ClosedTradesDataModel(TradeManager tradeManager, User user) {
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.user = user; this.user = user;
this.mapChangeListener = change -> { tradesListChangeListener = change -> applyList();
if (change.wasAdded())
list.add(new ClosedTradesListItem(change.getValueAdded()));
else if (change.wasRemoved())
list.removeIf(e -> e.getTrade().getId().equals(change.getValueRemoved().getId()));
};
} }
@Override @Override
public void activate() { public void activate() {
list.clear(); applyList();
tradeManager.getClosedTrades().values().stream() tradeManager.getClosedTrades().addListener(tradesListChangeListener);
.forEach(e -> list.add(new ClosedTradesListItem(e)));
tradeManager.getClosedTrades().addListener(mapChangeListener);
// We sort by date, earliest first
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate()));
} }
@Override @Override
public void deactivate() { public void deactivate() {
tradeManager.getClosedTrades().removeListener(mapChangeListener); tradeManager.getClosedTrades().removeListener(tradesListChangeListener);
} }
public ObservableList<ClosedTradesListItem> getList() { public ObservableList<ClosedTradesListItem> getList() {
@ -78,4 +69,13 @@ class ClosedTradesDataModel implements Activatable, DataModel {
offer.getDirection() : offer.getMirroredDirection(); offer.getDirection() : offer.getMirroredDirection();
} }
private void applyList() {
list.clear();
list.addAll(tradeManager.getClosedTrades().stream().map(ClosedTradesListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate()));
}
} }

View file

@ -41,6 +41,7 @@
<TableColumn text="Price" fx:id="priceColumn" minWidth="100" sortable="false"/> <TableColumn text="Price" fx:id="priceColumn" minWidth="100" sortable="false"/>
<TableColumn text="Trade amount in EUR" fx:id="volumeColumn" minWidth="130" sortable="false"/> <TableColumn text="Trade amount in EUR" fx:id="volumeColumn" minWidth="130" sortable="false"/>
<TableColumn text="Trade type" fx:id="directionColumn" minWidth="80" sortable="false"/> <TableColumn text="Trade type" fx:id="directionColumn" minWidth="80" sortable="false"/>
<TableColumn text="State" fx:id="stateColumn" minWidth="80" sortable="false"/>
</columns> </columns>
</TableView> </TableView>

View file

@ -34,7 +34,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<GridPane, ClosedTr
@FXML TableView<ClosedTradesListItem> table; @FXML TableView<ClosedTradesListItem> table;
@FXML TableColumn<ClosedTradesListItem, ClosedTradesListItem> priceColumn, amountColumn, volumeColumn, @FXML TableColumn<ClosedTradesListItem, ClosedTradesListItem> priceColumn, amountColumn, volumeColumn,
directionColumn, dateColumn, tradeIdColumn; directionColumn, dateColumn, tradeIdColumn, stateColumn;
@Inject @Inject
public ClosedTradesView(ClosedTradesViewModel model) { public ClosedTradesView(ClosedTradesViewModel model) {
@ -49,6 +49,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<GridPane, ClosedTr
setPriceColumnCellFactory(); setPriceColumnCellFactory();
setVolumeColumnCellFactory(); setVolumeColumnCellFactory();
setDateColumnCellFactory(); setDateColumnCellFactory();
setStateColumnCellFactory();
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setPlaceholder(new Label("No closed trades available")); table.setPlaceholder(new Label("No closed trades available"));
@ -121,6 +122,28 @@ public class ClosedTradesView extends ActivatableViewAndModel<GridPane, ClosedTr
}); });
} }
private void setStateColumnCellFactory() {
stateColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
stateColumn.setCellFactory(
new Callback<TableColumn<ClosedTradesListItem, ClosedTradesListItem>, TableCell<ClosedTradesListItem,
ClosedTradesListItem>>() {
@Override
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<ClosedTradesListItem, ClosedTradesListItem>() {
@Override
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(model.getState(item));
else
setText("");
}
};
}
});
}
private void setAmountColumnCellFactory() { private void setAmountColumnCellFactory() {
amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));

View file

@ -65,4 +65,24 @@ class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTradesDataMod
return formatter.formatDateTime(item.getTrade().getDate()); return formatter.formatDateTime(item.getTrade().getDate());
} }
String getState(ClosedTradesListItem item) {
if (item != null && item.getTrade() != null) {
switch (item.getTrade().getLifeCycleState()) {
case CANCELED:
return "Canceled";
case COMPLETED:
return "Completed";
case FAILED:
return "Failed";
case OPEN_OFFER:
case PENDING:
throw new RuntimeException("Wrong state: " + item.getTrade().getLifeCycleState());
}
return "Undefined";
}
else {
return "";
}
}
} }

View file

@ -32,7 +32,7 @@ import com.google.inject.Inject;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -45,40 +45,29 @@ class OffersDataModel implements Activatable, DataModel {
private final User user; private final User user;
private final ObservableList<OfferListItem> list = FXCollections.observableArrayList(); private final ObservableList<OfferListItem> list = FXCollections.observableArrayList();
private final MapChangeListener<String, Trade> offerMapChangeListener; private final ListChangeListener<Trade> tradesListChangeListener;
@Inject @Inject
public OffersDataModel(TradeManager tradeManager, User user) { public OffersDataModel(TradeManager tradeManager, User user) {
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
this.user = user; this.user = user;
this.offerMapChangeListener = change -> { tradesListChangeListener = change -> applyList();
if (change.wasAdded())
list.add(new OfferListItem(change.getValueAdded()));
else if (change.wasRemoved())
list.removeIf(e -> e.getOffer().getId().equals(change.getValueRemoved().getId()));
};
} }
@Override @Override
public void activate() { public void activate() {
list.clear(); applyList();
list.addAll(tradeManager.getOpenOfferTrades().values().stream().map(OfferListItem::new).collect(Collectors.toList())); tradeManager.getOpenOfferTrades().addListener(tradesListChangeListener);
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getOffer().getCreationDate().compareTo(o1.getOffer().getCreationDate()));
tradeManager.getOpenOfferTrades().addListener(offerMapChangeListener);
} }
@Override @Override
public void deactivate() { public void deactivate() {
tradeManager.getOpenOfferTrades().removeListener(offerMapChangeListener); tradeManager.getOpenOfferTrades().removeListener(tradesListChangeListener);
} }
void removeOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void cancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
tradeManager.removeOpenOffer(offer, resultHandler, errorMessageHandler); tradeManager.cancelOpenOffer(offer, resultHandler, errorMessageHandler);
} }
@ -90,4 +79,13 @@ class OffersDataModel implements Activatable, DataModel {
return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ? return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ?
offer.getDirection() : offer.getMirroredDirection(); offer.getDirection() : offer.getMirroredDirection();
} }
private void applyList() {
list.clear();
list.addAll(tradeManager.getOpenOfferTrades().stream().map(OfferListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getOffer().getCreationDate().compareTo(o1.getOffer().getCreationDate()));
}
} }

View file

@ -63,8 +63,8 @@ public class OffersView extends ActivatableViewAndModel<GridPane, OffersViewMode
table.setItems(model.getList()); table.setItems(model.getList());
} }
private void removeOpenOffer(Offer offer) { private void cancelOpenOffer(Offer offer) {
model.removeOpenOffer(offer); model.cancelOpenOffer(offer);
} }
private void openOfferDetails(OfferListItem item) { private void openOfferDetails(OfferListItem item) {
@ -233,7 +233,7 @@ public class OffersView extends ActivatableViewAndModel<GridPane, OffersViewMode
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null) { if (item != null) {
button.setOnAction(event -> removeOpenOffer(item.getOffer())); button.setOnAction(event -> cancelOpenOffer(item.getOffer()));
setGraphic(button); setGraphic(button);
} }
else { else {

View file

@ -44,8 +44,8 @@ class OffersViewModel extends ActivatableWithDataModel<OffersDataModel> implemen
} }
void removeOpenOffer(Offer offer) { void cancelOpenOffer(Offer offer) {
dataModel.removeOpenOffer(offer, dataModel.cancelOpenOffer(offer,
() -> { () -> {
// visual feedback? // visual feedback?
log.debug("Remove offer was successful"); log.debug("Remove offer was successful");

View file

@ -37,6 +37,8 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.stream.Collectors;
import javafx.beans.property.IntegerProperty; import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleIntegerProperty;
@ -45,7 +47,7 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -67,7 +69,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
private Trade closedTrade; private Trade closedTrade;
private final ChangeListener<Trade.ProcessState> tradeStateChangeListener; private final ChangeListener<Trade.ProcessState> tradeStateChangeListener;
private final MapChangeListener<String, Trade> mapChangeListener; private final ListChangeListener<Trade> tradesListChangeListener;
final StringProperty txId = new SimpleStringProperty(); final StringProperty txId = new SimpleStringProperty();
final ObjectProperty<Trade.ProcessState> tradeState = new SimpleObjectProperty<>(); final ObjectProperty<Trade.ProcessState> tradeState = new SimpleObjectProperty<>();
@ -80,49 +82,14 @@ class PendingTradesDataModel implements Activatable, DataModel {
this.user = user; this.user = user;
tradeStateChangeListener = (ov, oldValue, newValue) -> tradeState.set(newValue); tradeStateChangeListener = (ov, oldValue, newValue) -> tradeState.set(newValue);
tradesListChangeListener = change -> applyList();
mapChangeListener = change -> {
if (change.wasAdded()) {
list.add(new PendingTradesListItem(change.getValueAdded()));
if (list.size() == 1) {
selectTrade(list.get(0));
selectedIndex.set(0);
}
}
else if (change.wasRemoved()) {
closedTrade = change.getValueRemoved();
if (list.size() == 0) {
selectTrade(null);
selectedIndex.set(-1);
}
}
sortList();
};
} }
@Override @Override
public void activate() { public void activate() {
list.clear(); applyList();
// transform trades to list of PendingTradesListItems and keep it updated tradeManager.getPendingTrades().addListener(tradesListChangeListener);
tradeManager.getPendingTrades().values().stream().forEach(e -> list.add(new PendingTradesListItem(e)));
tradeManager.getPendingTrades().addListener(mapChangeListener);
// we sort by date, earliest first
sortList();
// select either currentPendingTrade or first in the list
/*if (tradeManager.getCurrentPendingTrade() != null) {
for (int i = 0; i < list.size(); i++) {
PendingTradesListItem item = list.get(i);
if (tradeManager.getCurrentPendingTrade().getId().equals(item.getTrade().getId())) {
selectedIndex.set(i);
selectTrade(item);
break;
}
}
}
else */
if (list.size() > 0) { if (list.size() > 0) {
selectTrade(list.get(0)); selectTrade(list.get(0));
selectedIndex.set(0); selectedIndex.set(0);
@ -131,11 +98,17 @@ class PendingTradesDataModel implements Activatable, DataModel {
@Override @Override
public void deactivate() { public void deactivate() {
tradeManager.getPendingTrades().removeListener(mapChangeListener); tradeManager.getPendingTrades().removeListener(tradesListChangeListener);
cleanUpSelectedTrade(); cleanUpSelectedTrade();
} }
private void applyList() {
list.clear();
list.addAll(tradeManager.getPendingTrades().stream().map(PendingTradesListItem::new).collect(Collectors.toList()));
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate()));
}
void selectTrade(PendingTradesListItem item) { void selectTrade(PendingTradesListItem item) {
// clean up previous selectedItem // clean up previous selectedItem
@ -282,9 +255,5 @@ class PendingTradesDataModel implements Activatable, DataModel {
} }
} }
private void sortList() {
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate()));
}
} }

View file

@ -471,8 +471,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
if (model.isMyOffer(offer)) { if (model.isMyOffer(offer)) {
iconView.setId("image-remove"); iconView.setId("image-remove");
title = "Remove"; title = "Remove";
button.setOnAction(event -> model.cancelOpenOffer(item button.setOnAction(event -> model.cancelOpenOffer(item.getOffer()));
.getOffer()));
} }
else { else {
if (offer.getDirection() == Direction.SELL) if (offer.getDirection() == Direction.SELL)
@ -484,8 +483,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
} }
//TODO remove listener //TODO remove listener
item.bankAccountCountryProperty().addListener((ov, o, n) -> item.bankAccountCountryProperty().addListener((ov, o, n) -> verifyIfTradable(item));
verifyIfTradable(item));
verifyIfTradable(item); verifyIfTradable(item);
button.setText(title); button.setText(title);

View file

@ -53,14 +53,16 @@ import org.bitcoinj.utils.Fiat;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableMap; import javafx.collections.ObservableList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -84,8 +86,9 @@ public class TradeManager {
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>(); private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>();
private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>(); private final Map<String, CheckOfferAvailabilityProtocol> checkOfferAvailabilityProtocolMap = new HashMap<>();
private final Map<String, Trade> trades = new HashMap<>(); private final ObservableList<Trade> openOfferTrades = FXCollections.observableArrayList();
private final ObservableList<Trade> pendingTrades = FXCollections.observableArrayList();
private final ObservableList<Trade> closedTrades = FXCollections.observableArrayList();
private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>(); private final Map<String, MailboxMessage> mailboxMessages = new HashMap<>();
@ -110,9 +113,17 @@ public class TradeManager {
this.encryptionService = encryptionService; this.encryptionService = encryptionService;
this.offerBookService = offerBookService; this.offerBookService = offerBookService;
Serializable tradesObject = persistence.read(this, "trades"); Serializable openOffersObject = persistence.read(this, "openOffers");
if (tradesObject instanceof Map<?, ?>) { if (openOffersObject instanceof List<?>) {
trades.putAll((Map<String, Trade>) tradesObject); openOfferTrades.addAll((List<Trade>) openOffersObject);
}
Serializable pendingTradesObject = persistence.read(this, "pendingTrades");
if (pendingTradesObject instanceof List<?>) {
pendingTrades.addAll((List<Trade>) pendingTradesObject);
}
Serializable closedTradesObject = persistence.read(this, "closedTrades");
if (closedTradesObject instanceof List<?>) {
closedTrades.addAll((List<Trade>) closedTradesObject);
} }
} }
@ -124,11 +135,13 @@ public class TradeManager {
// When all services are initialized we create the protocols for our open offers and persisted not completed pendingTrades // 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. // BuyerAcceptsOfferProtocol listens for take offer requests, so we need to instantiate it early.
public void onAllServicesInitialized() { public void onAllServicesInitialized() {
for (Map.Entry<String, Trade> entry : trades.entrySet()) { for (Trade trade : openOfferTrades) {
createOffererAsBuyerProtocol(trade);
}
for (Trade trade : pendingTrades) {
// We continue an interrupted trade. // We continue an interrupted trade.
// TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to // TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to
// continue the trade, but that might fail. // continue the trade, but that might fail.
Trade trade = entry.getValue();
if (isMyOffer(trade.getOffer())) { if (isMyOffer(trade.getOffer())) {
createOffererAsBuyerProtocol(trade); createOffererAsBuyerProtocol(trade);
} }
@ -182,8 +195,11 @@ public class TradeManager {
PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol( PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol(
model, model,
(transaction) -> { (transaction) -> {
Trade trade = createTrade(offer); Trade trade = new Trade(offer);
trade.setLifeCycleState(Trade.LifeCycleState.OPEN_OFFER); trade.setLifeCycleState(Trade.LifeCycleState.OPEN_OFFER);
openOfferTrades.add(trade);
persistOpenOfferTrades();
createOffererAsBuyerProtocol(trade); createOffererAsBuyerProtocol(trade);
resultHandler.handleResult(transaction); resultHandler.handleResult(transaction);
}, },
@ -194,11 +210,6 @@ public class TradeManager {
} }
public void cancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void cancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
trades.get(offer.getId()).setLifeCycleState(Trade.LifeCycleState.CANCELED);
removeOpenOffer(offer, resultHandler, errorMessageHandler, true);
}
public void removeOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
removeOpenOffer(offer, resultHandler, errorMessageHandler, true); removeOpenOffer(offer, resultHandler, errorMessageHandler, true);
} }
@ -254,7 +265,7 @@ public class TradeManager {
// TODO remove if check when persistence is impl. // TODO remove if check when persistence is impl.
if (offererAsBuyerProtocolMap.containsKey(tradeId)) { if (offererAsBuyerProtocolMap.containsKey(tradeId)) {
offererAsBuyerProtocolMap.get(tradeId).onFiatPaymentStarted(); offererAsBuyerProtocolMap.get(tradeId).onFiatPaymentStarted();
persistTrades(); persistPendingTrades();
} }
} }
@ -264,6 +275,10 @@ public class TradeManager {
public void onWithdrawAtTradeCompleted(Trade trade) { public void onWithdrawAtTradeCompleted(Trade trade) {
trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED); trade.setLifeCycleState(Trade.LifeCycleState.COMPLETED);
pendingTrades.remove(trade);
persistPendingTrades();
closedTrades.add(trade);
persistClosedTrades();
removeFromProtocolMap(trade); removeFromProtocolMap(trade);
} }
@ -277,27 +292,20 @@ public class TradeManager {
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public ObservableMap<String, Trade> getOpenOfferTrades() { public ObservableList<Trade> getOpenOfferTrades() {
ObservableMap<String, Trade> list = FXCollections.observableHashMap(); return openOfferTrades;
list.putAll(trades);
return list;
} }
public ObservableMap<String, Trade> getPendingTrades() { public ObservableList<Trade> getPendingTrades() {
ObservableMap<String, Trade> list = FXCollections.observableHashMap(); return pendingTrades;
list.putAll(trades);
return list;
} }
public ObservableMap<String, Trade> getClosedTrades() { public ObservableList<Trade> getClosedTrades() {
ObservableMap<String, Trade> list = FXCollections.observableHashMap(); return closedTrades;
list.putAll(trades);
return list;
} }
@ -312,16 +320,27 @@ public class TradeManager {
private void removeOpenOffer(Offer offer, private void removeOpenOffer(Offer offer,
ResultHandler resultHandler, ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler, ErrorMessageHandler errorMessageHandler,
boolean removeFromBuyerAcceptsOfferProtocolMap) { boolean isCancelRequest) {
String offerId = offer.getId();
offerBookService.removeOffer(offer, offerBookService.removeOffer(offer,
() -> { () -> {
offer.setState(Offer.State.REMOVED); offer.setState(Offer.State.REMOVED);
trades.remove(offerId);
Optional<Trade> result = openOfferTrades.stream().filter(e -> e.getId().equals(offer.getId())).findAny();
if (result.isPresent()) {
Trade trade = result.get();
openOfferTrades.remove(trade);
persistOpenOfferTrades();
if (isCancelRequest) {
trade.setLifeCycleState(Trade.LifeCycleState.CANCELED);
closedTrades.add(trade);
persistClosedTrades();
}
}
disposeCheckOfferAvailabilityRequest(offer); disposeCheckOfferAvailabilityRequest(offer);
persistTrades(); String offerId = offer.getId();
if (removeFromBuyerAcceptsOfferProtocolMap && offererAsBuyerProtocolMap.containsKey(offerId)) { if (isCancelRequest && offererAsBuyerProtocolMap.containsKey(offerId)) {
offererAsBuyerProtocolMap.get(offerId).cleanup(); offererAsBuyerProtocolMap.get(offerId).cleanup();
offererAsBuyerProtocolMap.remove(offerId); offererAsBuyerProtocolMap.remove(offerId);
} }
@ -351,21 +370,18 @@ public class TradeManager {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private Trade takeAvailableOffer(Coin amount, Offer offer, Peer peer) { private Trade takeAvailableOffer(Coin amount, Offer offer, Peer peer) {
Trade trade = createTrade(offer); Trade trade = new Trade(offer);
trade.setTradeAmount(amount); trade.setTradeAmount(amount);
trade.setTradingPeer(peer); trade.setTradingPeer(peer);
trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
pendingTrades.add(trade);
persistPendingTrades();
TakerAsSellerProtocol sellerTakesOfferProtocol = createTakerAsSellerProtocol(trade); TakerAsSellerProtocol sellerTakesOfferProtocol = createTakerAsSellerProtocol(trade);
sellerTakesOfferProtocol.takeAvailableOffer(); sellerTakesOfferProtocol.takeAvailableOffer();
return trade; return trade;
} }
private Trade createTrade(Offer offer) {
Trade trade = new Trade(offer);
trades.put(trade.getId(), trade);
persistTrades();
return trade;
}
private TakerAsSellerProtocol createTakerAsSellerProtocol(Trade trade) { private TakerAsSellerProtocol createTakerAsSellerProtocol(Trade trade) {
trade.processStateProperty().addListener((ov, oldValue, newValue) -> { trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
@ -379,7 +395,7 @@ public class TradeManager {
case FIAT_PAYMENT_STARTED: case FIAT_PAYMENT_STARTED:
case FIAT_PAYMENT_RECEIVED: case FIAT_PAYMENT_RECEIVED:
case PAYOUT_PUBLISHED: case PAYOUT_PUBLISHED:
persistTrades(); persistPendingTrades();
break; break;
case MESSAGE_SENDING_FAILED: case MESSAGE_SENDING_FAILED:
case FAULT: case FAULT:
@ -432,19 +448,22 @@ public class TradeManager {
case INIT: case INIT:
break; break;
case TAKE_OFFER_FEE_TX_CREATED: case TAKE_OFFER_FEE_TX_CREATED:
persistTrades(); persistPendingTrades();
break; break;
case DEPOSIT_PUBLISHED: case DEPOSIT_PUBLISHED:
removeOpenOffer(trade.getOffer(), removeOpenOffer(trade.getOffer(),
() -> log.debug("remove offer was successful"), () -> log.debug("remove offer was successful"),
(message) -> log.error(message), (message) -> log.error(message),
false); false);
model.trade.setLifeCycleState(Trade.LifeCycleState.PENDING);
pendingTrades.add(trade);
persistPendingTrades();
break; break;
case DEPOSIT_CONFIRMED: case DEPOSIT_CONFIRMED:
case FIAT_PAYMENT_STARTED: case FIAT_PAYMENT_STARTED:
case FIAT_PAYMENT_RECEIVED: case FIAT_PAYMENT_RECEIVED:
case PAYOUT_PUBLISHED: case PAYOUT_PUBLISHED:
persistTrades(); persistPendingTrades();
break; break;
case TAKE_OFFER_FEE_PUBLISH_FAILED: case TAKE_OFFER_FEE_PUBLISH_FAILED:
case MESSAGE_SENDING_FAILED: case MESSAGE_SENDING_FAILED:
@ -520,8 +539,16 @@ public class TradeManager {
}); });
} }
private void persistTrades() { private void persistOpenOfferTrades() {
persistence.write(this, "trades", (Map<String, Trade>) new HashMap<>(trades)); persistence.write(this, "openOfferTrades", (List<Trade>) new ArrayList<>(openOfferTrades));
}
private void persistPendingTrades() {
persistence.write(this, "pendingTrades", (List<Trade>) new ArrayList<>(pendingTrades));
}
private void persistClosedTrades() {
persistence.write(this, "closedTrades", (List<Trade>) new ArrayList<>(closedTrades));
} }
} }