Use combo box for Make market price, update all prices on interval

This commit is contained in:
Manfred Karrer 2016-04-07 15:39:45 +02:00
parent 0ebf3f6b36
commit 3f5e1296b1
11 changed files with 251 additions and 89 deletions

View file

@ -76,7 +76,7 @@ import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
public class BitsquareApp extends Application {
private static final Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(BitsquareApp.class);
public static final boolean DEV_MODE = false;
public static final boolean DEV_MODE = true;
public static final boolean IS_RELEASE_VERSION = !DEV_MODE && true;
private static Environment env;

View file

@ -1000,13 +1000,16 @@ textfield */
-fx-text-fill: #dd6900;
}
#price-feed-text-field {
-fx-alignment: center;
#price-feed-combo {
-fx-background-color: #555;
-fx-text-fill: white;
-fx-cursor: hand;
}
#invert-market-price {
-fx-text-fill: #111;
}
#popup-qr-code-info {
-fx-font-size: 11;
}
}

View file

@ -17,6 +17,8 @@
package io.bitsquare.gui.main;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.BitsquareException;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.pricefeed.PriceFeed;
@ -36,8 +38,6 @@ import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.settings.SettingsView;
import io.bitsquare.gui.util.Transitions;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@ -47,8 +47,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.monadic.MonadicBinding;
import javax.inject.Inject;
import javax.inject.Named;
@ -61,7 +59,6 @@ import static javafx.scene.layout.AnchorPane.*;
public class MainView extends InitializableView<StackPane, MainViewModel> {
public static final String TITLE_KEY = "view.title";
private MonadicBinding<String> marketPriceBinding;
public static StackPane getRootContainer() {
return MainView.rootContainer;
@ -92,7 +89,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private final ViewLoader viewLoader;
private final Navigation navigation;
private static Transitions transitions;
private final String title;
private ChangeListener<String> walletServiceErrorMsgListener;
private ChangeListener<String> btcSyncIconIdListener;
private ChangeListener<String> splashP2PNetworkErrorMsgListener;
@ -106,6 +102,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private BorderPane baseApplicationContainer;
private Overlay<Popup> p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup;
private static StackPane rootContainer;
private ChangeListener<PriceFeedComboBoxItem> selectedPriceFeedItemListender;
@Inject
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, Transitions transitions,
@ -114,7 +111,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
this.viewLoader = viewLoader;
this.navigation = navigation;
MainView.transitions = transitions;
this.title = title;
}
@Override
@ -138,22 +134,19 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
setTopAnchor(this, 0d);
}};
Tuple3<TextField, Label, VBox> marketPriceBox = getMarketPriceBox("Market price");
final BooleanProperty priceInverted = new SimpleBooleanProperty(false);
marketPriceBox.first.setOnMouseClicked(e -> priceInverted.setValue(!priceInverted.get()));
marketPriceBinding = EasyBind.combine(
model.marketPriceCurrency, model.marketPrice, model.marketPriceInverted, priceInverted,
(marketPriceCurrency, marketPrice, marketPriceInverted, inverted) ->
(priceInverted.get() ?
marketPriceInverted :
marketPrice) +
(priceInverted.get() ?
" BTC/" + marketPriceCurrency :
" " + marketPriceCurrency + "/BTC"));
Tuple3<ComboBox<PriceFeedComboBoxItem>, Label, VBox> marketPriceBox = getMarketPriceBox("Market price");
ComboBox<PriceFeedComboBoxItem> priceComboBox = marketPriceBox.first;
priceComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
model.setPriceFeedComboBoxItem(newValue);
marketPriceBinding.subscribe((observable, oldValue, newValue) -> {
marketPriceBox.first.setText(newValue);
});
selectedPriceFeedItemListender = (observable, oldValue, newValue) -> {
priceComboBox.getSelectionModel().select(newValue);
};
model.selectedPriceFeedComboBoxItemProperty.addListener(selectedPriceFeedItemListender);
priceComboBox.setItems(model.priceFeedComboBoxItems);
marketPriceBox.second.textProperty().bind(createStringBinding(
() -> {
@ -268,22 +261,56 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
return new Tuple2(textField, vBox);
}
private Tuple3<TextField, Label, VBox> getMarketPriceBox(String text) {
TextField textField = new TextField();
textField.setEditable(false);
textField.setPrefWidth(180);
textField.setFocusTraversable(false);
textField.setId("price-feed-text-field");
private ListCell<PriceFeedComboBoxItem> getPriceFeedComboBoxListCell() {
return new ListCell<PriceFeedComboBoxItem>() {
@Override
protected void updateItem(PriceFeedComboBoxItem item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
textProperty().bind(item.displayStringProperty);
} else {
textProperty().unbind();
}
}
};
}
private Tuple3<ComboBox<PriceFeedComboBoxItem>, Label, VBox> getMarketPriceBox(String text) {
ComboBox<PriceFeedComboBoxItem> priceComboBox = new ComboBox<>();
priceComboBox.setVisibleRowCount(40);
priceComboBox.setMaxWidth(210);
priceComboBox.setMinWidth(210);
priceComboBox.setFocusTraversable(false);
priceComboBox.setId("price-feed-combo");
priceComboBox.setCellFactory(p -> getPriceFeedComboBoxListCell());
ListCell<PriceFeedComboBoxItem> buttonCell = getPriceFeedComboBoxListCell();
buttonCell.setId("price-feed-combo");
priceComboBox.setButtonCell(buttonCell);
Label invertIcon = new Label();
HBox.setMargin(invertIcon, new Insets(3, 0, 0, 0));
invertIcon.setId("invert-market-price");
invertIcon.setOpacity(0.8);
invertIcon.setOnMouseClicked(e -> {
model.preferences.flipUseInvertedMarketPrice();
});
HBox hBox = new HBox();
hBox.setSpacing(5);
AwesomeDude.setIcon(invertIcon, AwesomeIcon.RETWEET, "14.0");
hBox.getChildren().setAll(priceComboBox, invertIcon);
Label label = new Label(text);
label.setId("nav-balance-label");
label.setPadding(new Insets(0, 5, 0, 5));
label.setPrefWidth(textField.getPrefWidth());
label.setTextAlignment(TextAlignment.CENTER);
label.setPadding(new Insets(0, 25, 0, 5));
label.prefWidthProperty().bind(hBox.widthProperty());
VBox vBox = new VBox();
vBox.setSpacing(3);
vBox.setPadding(new Insets(11, 0, 0, 0));
vBox.getChildren().addAll(textField, label);
return new Tuple3(textField, label, vBox);
vBox.getChildren().addAll(hBox, label);
return new Tuple3(priceComboBox, label, vBox);
}
public void setPersistedFilesCorrupted(List<String> persistedFilesCorrupted) {

View file

@ -30,6 +30,7 @@ import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.MarketPrice;
import io.bitsquare.btc.pricefeed.PriceFeed;
import io.bitsquare.common.Clock;
import io.bitsquare.common.Timer;
@ -48,6 +49,7 @@ import io.bitsquare.gui.main.overlays.windows.TacWindow;
import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.P2PServiceListener;
import io.bitsquare.p2p.network.CloseConnectionReason;
@ -57,14 +59,14 @@ import io.bitsquare.p2p.peers.keepalive.messages.Ping;
import io.bitsquare.payment.OKPayAccount;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.failed.FailedTradesManager;
import io.bitsquare.trade.offer.OpenOffer;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
@ -76,10 +78,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
@ -94,7 +93,7 @@ public class MainViewModel implements ViewModel {
private final TradeManager tradeManager;
private final OpenOfferManager openOfferManager;
private final DisputeManager disputeManager;
private final Preferences preferences;
final Preferences preferences;
private final AlertManager alertManager;
private final WalletPasswordWindow walletPasswordWindow;
private final NotificationCenter notificationCenter;
@ -113,6 +112,7 @@ public class MainViewModel implements ViewModel {
final StringProperty marketPriceInverted = new SimpleStringProperty("N/A");
final StringProperty marketPriceCurrency = new SimpleStringProperty("");
final ObjectProperty<PriceFeed.Type> typeProperty = new SimpleObjectProperty<>(PriceFeed.Type.LAST);
final ObjectProperty<PriceFeedComboBoxItem> selectedPriceFeedComboBoxItemProperty = new SimpleObjectProperty<>();
final StringProperty availableBalance = new SimpleStringProperty();
final StringProperty reservedBalance = new SimpleStringProperty();
final StringProperty lockedBalance = new SimpleStringProperty();
@ -139,15 +139,16 @@ public class MainViewModel implements ViewModel {
final StringProperty p2pNetworkLabelId = new SimpleStringProperty("footer-pane");
private MonadicBinding<Boolean> allServicesDone, tradesAndUIReady;
private final PriceFeed priceFeed;
private final ClosedTradableManager closedTradableManager;
private final FailedTradesManager failedTradesManager;
final PriceFeed priceFeed;
private final User user;
private int numBtcPeers = 0;
private Timer checkNumberOfBtcPeersTimer;
private Timer checkNumberOfP2pNetworkPeersTimer;
private Timer startupTimeout;
private final Map<String, Subscription> disputeIsClosedSubscriptionsMap = new HashMap<>();
final ObservableList<PriceFeedComboBoxItem> priceFeedComboBoxItems = FXCollections.observableArrayList();
private MonadicBinding<String> marketPriceBinding;
private Subscription priceFeedAllLoadedSubscription;
///////////////////////////////////////////////////////////////////////////////////////////
@ -158,14 +159,11 @@ public class MainViewModel implements ViewModel {
public MainViewModel(WalletService walletService, TradeWalletService tradeWalletService,
PriceFeed priceFeed,
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
OpenOfferManager openOfferManager, ClosedTradableManager closedTradableManager,
FailedTradesManager failedTradesManager, DisputeManager disputeManager, Preferences preferences,
OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
User user, AlertManager alertManager, WalletPasswordWindow walletPasswordWindow,
NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock,
KeyRing keyRing, Navigation navigation, BSFormatter formatter) {
this.priceFeed = priceFeed;
this.closedTradableManager = closedTradableManager;
this.failedTradesManager = failedTradesManager;
this.user = user;
this.walletService = walletService;
this.tradeWalletService = tradeWalletService;
@ -472,6 +470,7 @@ public class MainViewModel implements ViewModel {
setupDevDummyPaymentAccount();
setupMarketPriceFeed();
swapPendingOfferFundingEntries();
fillPriceFeedComboBoxItems();
showAppScreen.set(true);
@ -666,7 +665,7 @@ public class MainViewModel implements ViewModel {
priceFeed.setType(PriceFeed.Type.LAST);
priceFeed.init(price -> {
marketPrice.set(formatter.formatMarketPrice(price));
marketPriceInverted.set(formatter.formatMarketPrice(1 / price, 8));
marketPriceInverted.set(price != 0 ? formatter.formatMarketPrice(1 / price, 8) : "");
},
(errorMessage, throwable) -> {
marketPrice.set("N/A");
@ -674,6 +673,74 @@ public class MainViewModel implements ViewModel {
});
marketPriceCurrency.bind(priceFeed.currencyCodeProperty());
typeProperty.bind(priceFeed.typeProperty());
marketPriceBinding = EasyBind.combine(
marketPriceCurrency, marketPrice, marketPriceInverted, preferences.useInvertedMarketPriceProperty(),
(marketPriceCurrency, marketPrice, marketPriceInverted, useInvertedMarketPrice) ->
(useInvertedMarketPrice ? marketPriceInverted : marketPrice) +
(useInvertedMarketPrice ? " BTC/" + marketPriceCurrency : " " + marketPriceCurrency + "/BTC"));
marketPriceBinding.subscribe((observable, oldValue, newValue) -> {
if (selectedPriceFeedComboBoxItemProperty.get() == null) {
findPriceFeedComboBoxItem(preferences.getPreferredTradeCurrency().getCode())
.ifPresent(item -> {
item.setDisplayString(newValue);
selectedPriceFeedComboBoxItemProperty.set(item);
});
}
if (selectedPriceFeedComboBoxItemProperty.get() != null)
selectedPriceFeedComboBoxItemProperty.get().setDisplayString(newValue);
});
priceFeedAllLoadedSubscription = EasyBind.subscribe(priceFeed.currenciesUpdateFlagProperty(), newPriceUpdate -> {
priceFeedComboBoxItems.stream().forEach(item -> {
String currencyCode = item.currencyCode;
MarketPrice marketPrice = priceFeed.getMarketPrice(currencyCode);
boolean useInvertedMarketPrice = preferences.getUseInvertedMarketPrice();
String priceString;
String currencyPairString = useInvertedMarketPrice ? "BTC/" + currencyCode : currencyCode + "/BTC";
if (marketPrice != null) {
double price = marketPrice.getPrice(priceFeed.getType());
if (price != 0) {
double priceInverted = 1 / price;
priceString = useInvertedMarketPrice ? formatter.formatMarketPrice(priceInverted, 8) : formatter.formatMarketPrice(price);
} else {
priceString = "N/A";
}
} else {
priceString = "N/A";
}
item.setDisplayString(priceString + " " + currencyPairString);
});
});
preferences.getTradeCurrenciesAsObservable().addListener((ListChangeListener<TradeCurrency>) c -> {
UserThread.runAfter(() -> fillPriceFeedComboBoxItems(), 100, TimeUnit.MILLISECONDS);
});
}
public void setPriceFeedComboBoxItem(PriceFeedComboBoxItem item) {
if (item != null) {
selectedPriceFeedComboBoxItemProperty.set(item);
priceFeed.setCurrencyCode(item.currencyCode);
} else {
findPriceFeedComboBoxItem(preferences.getPreferredTradeCurrency().getCode())
.ifPresent(item2 -> selectedPriceFeedComboBoxItemProperty.set(item2));
}
}
Optional<PriceFeedComboBoxItem> findPriceFeedComboBoxItem(String currencyCode) {
return priceFeedComboBoxItems.stream()
.filter(item -> item.currencyCode.equals(currencyCode))
.findAny();
}
private void fillPriceFeedComboBoxItems() {
List<PriceFeedComboBoxItem> currencyItems = preferences.getTradeCurrenciesAsObservable()
.stream()
.map(tradeCurrency -> new PriceFeedComboBoxItem(tradeCurrency.getCode()))
.collect(Collectors.toList());
priceFeedComboBoxItems.setAll(currencyItems);
}
private void displayAlertIfPresent(Alert alert) {

View file

@ -0,0 +1,25 @@
package io.bitsquare.gui.main;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PriceFeedComboBoxItem {
private static final Logger log = LoggerFactory.getLogger(PriceFeedComboBoxItem.class);
public final String currencyCode;
public final StringProperty displayStringProperty = new SimpleStringProperty();
public PriceFeedComboBoxItem(String currencyCode) {
this.currencyCode = currencyCode;
}
public String getDisplayString() {
return displayStringProperty.get();
}
public void setDisplayString(String displayString) {
this.displayStringProperty.set(displayString);
}
}

View file

@ -74,7 +74,7 @@ class MarketsChartsViewModel extends ActivatableViewModel {
offerBookListItems.addListener(listChangeListener);
offerBook.fillOfferBookListItems();
updateChartData(offerBookListItems);
priceFeed.setCurrencyCode(tradeCurrency.get().getCode());
//priceFeed.setCurrencyCode(tradeCurrency.get().getCode());
}
@Override
@ -136,7 +136,7 @@ class MarketsChartsViewModel extends ActivatableViewModel {
public void onSetTradeCurrency(TradeCurrency tradeCurrency) {
this.tradeCurrency.set(tradeCurrency);
updateChartData(offerBookListItems);
priceFeed.setCurrencyCode(tradeCurrency.getCode());
//priceFeed.setCurrencyCode(tradeCurrency.getCode());
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -175,8 +175,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
paymentAccounts.setAll(user.getPaymentAccounts());
if (isTabSelected)
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
/*if (isTabSelected)
priceFeed.setCurrencyCode(tradeCurrencyCode.get());*/
updateBalance();
}
@ -220,7 +220,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
if (account != null)
paymentAccount = account;
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
//priceFeed.setCurrencyCode(tradeCurrencyCode.get());
calculateVolume();
calculateTotalToPay();
@ -228,8 +228,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
void onTabSelected(boolean isSelected) {
this.isTabSelected = isSelected;
if (isTabSelected)
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
/*if (isTabSelected)
priceFeed.setCurrencyCode(tradeCurrencyCode.get());*/
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -299,7 +299,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
if (paymentAccount != null)
paymentAccount.setSelectedTradeCurrency(tradeCurrency);
priceFeed.setCurrencyCode(code);
//priceFeed.setCurrencyCode(code);
}
}

View file

@ -158,12 +158,12 @@ class OfferBookViewModel extends ActivatableViewModel {
}
private void setMarketPriceFeedCurrency() {
if (isTabSelected) {
/*if (isTabSelected) {
if (showAllTradeCurrenciesProperty.get())
priceFeed.setCurrencyCode(CurrencyUtil.getDefaultTradeCurrency().getCode());
else
priceFeed.setCurrencyCode(tradeCurrencyCode.get());
}
}*/
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -136,7 +136,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
// if (isWalletFunded.get())
// feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
if (isTabSelected)
/*if (isTabSelected)*/
priceFeed.setCurrencyCode(offer.getCurrencyCode());
tradeManager.checkOfferAvailability(offer, () -> {
@ -207,13 +207,13 @@ class TakeOfferDataModel extends ActivatableDataModel {
};
offer.resetState();
priceFeed.setCurrencyCode(offer.getCurrencyCode());
//priceFeed.setCurrencyCode(offer.getCurrencyCode());
}
void onTabSelected(boolean isSelected) {
this.isTabSelected = isSelected;
if (isTabSelected)
priceFeed.setCurrencyCode(offer.getCurrencyCode());
/*if (isTabSelected)
priceFeed.setCurrencyCode(offer.getCurrencyCode());*/
}