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

@ -8,13 +8,10 @@ import io.bitsquare.app.Log;
import io.bitsquare.btc.pricefeed.providers.BitcoinAveragePriceProvider; import io.bitsquare.btc.pricefeed.providers.BitcoinAveragePriceProvider;
import io.bitsquare.btc.pricefeed.providers.PoloniexPriceProvider; import io.bitsquare.btc.pricefeed.providers.PoloniexPriceProvider;
import io.bitsquare.btc.pricefeed.providers.PriceProvider; import io.bitsquare.btc.pricefeed.providers.PriceProvider;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import javafx.beans.property.ObjectProperty; import io.bitsquare.locale.CurrencyUtil;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.*;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -43,8 +40,10 @@ public class PriceFeed {
} }
} }
private static final long PERIOD_FIAT_SEC = Timer.STRESS_TEST ? 30 : 60; // We load only the selected currency on interval. Only the first request we load all private static final long PERIOD_FIAT_SEC = 60;
private static final long PERIOD_CRYPTO_SEC = Timer.STRESS_TEST ? 30 : 10 * 60; // We load the full list with 33kb so we don't want to load too often private static final long PERIOD_ALL_FIAT_SEC = 60 * 5;
private static final long PERIOD_CRYPTO_SEC = 60;
private static final long PERIOD_ALL_CRYPTO_SEC = 60 * 5;
private final Map<String, MarketPrice> cache = new HashMap<>(); private final Map<String, MarketPrice> cache = new HashMap<>();
private final PriceProvider fiatPriceProvider = new BitcoinAveragePriceProvider(); private final PriceProvider fiatPriceProvider = new BitcoinAveragePriceProvider();
@ -53,8 +52,9 @@ public class PriceFeed {
private FaultHandler faultHandler; private FaultHandler faultHandler;
private Type type; private Type type;
private String currencyCode; private String currencyCode;
transient private final StringProperty currencyCodeProperty = new SimpleStringProperty(); private final StringProperty currencyCodeProperty = new SimpleStringProperty();
transient private final ObjectProperty<Type> typeProperty = new SimpleObjectProperty<>(); private final ObjectProperty<Type> typeProperty = new SimpleObjectProperty<>();
private final IntegerProperty currenciesUpdateFlag = new SimpleIntegerProperty(0);
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -84,6 +84,10 @@ public class PriceFeed {
UserThread.runPeriodically(() -> requestAllPrices(cryptoCurrenciesPriceProvider, this::applyPrice), UserThread.runPeriodically(() -> requestAllPrices(cryptoCurrenciesPriceProvider, this::applyPrice),
PERIOD_CRYPTO_SEC); PERIOD_CRYPTO_SEC);
}); });
UserThread.runPeriodically(() -> requestAllPrices(fiatPriceProvider, this::applyPrice), PERIOD_ALL_FIAT_SEC);
UserThread.runPeriodically(() -> requestAllPrices(cryptoCurrenciesPriceProvider, this::applyPrice), PERIOD_ALL_CRYPTO_SEC);
requestAllPrices(cryptoCurrenciesPriceProvider, this::applyPrice); requestAllPrices(cryptoCurrenciesPriceProvider, this::applyPrice);
} }
@ -109,6 +113,11 @@ public class PriceFeed {
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
currencyCodeProperty.set(currencyCode); currencyCodeProperty.set(currencyCode);
applyPrice(); applyPrice();
if (CurrencyUtil.isFiatCurrency(currencyCode))
requestPrice(fiatPriceProvider);
else
requestPrice(cryptoCurrenciesPriceProvider);
} }
@ -132,6 +141,10 @@ public class PriceFeed {
return typeProperty; return typeProperty;
} }
public IntegerProperty currenciesUpdateFlagProperty() {
return currenciesUpdateFlag;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Private // Private
@ -142,6 +155,7 @@ public class PriceFeed {
if (cache.containsKey(currencyCode)) { if (cache.containsKey(currencyCode)) {
MarketPrice marketPrice = cache.get(currencyCode); MarketPrice marketPrice = cache.get(currencyCode);
//log.debug("applyPrice type=" + type); //log.debug("applyPrice type=" + type);
if (marketPrice != null)
priceConsumer.accept(marketPrice.getPrice(type)); priceConsumer.accept(marketPrice.getPrice(type));
} else { } else {
String errorMessage = "We don't have a price for currencyCode " + currencyCode; String errorMessage = "We don't have a price for currencyCode " + currencyCode;
@ -149,6 +163,7 @@ public class PriceFeed {
faultHandler.handleFault(errorMessage, new PriceRequestException(errorMessage)); faultHandler.handleFault(errorMessage, new PriceRequestException(errorMessage));
} }
} }
currenciesUpdateFlag.setValue(currenciesUpdateFlag.get() + 1);
} }
private void requestPrice(PriceProvider provider) { private void requestPrice(PriceProvider provider) {

View file

@ -83,7 +83,7 @@ public final class Preferences implements Persistable {
return defaultTradeCurrency; return defaultTradeCurrency;
} }
private static boolean _useAnimations = true; private static boolean staticUseAnimations = true;
transient private final Storage<Preferences> storage; transient private final Storage<Preferences> storage;
transient private final BitsquareEnvironment bitsquareEnvironment; transient private final BitsquareEnvironment bitsquareEnvironment;
@ -107,10 +107,12 @@ public final class Preferences implements Persistable {
private TradeCurrency preferredTradeCurrency; private TradeCurrency preferredTradeCurrency;
private long nonTradeTxFeePerKB = FeePolicy.getNonTradeFeePerKb().value; private long nonTradeTxFeePerKB = FeePolicy.getNonTradeFeePerKb().value;
private double maxPriceDistanceInPercent; private double maxPriceDistanceInPercent;
private boolean useInvertedMarketPrice;
// Observable wrappers // Observable wrappers
transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination); transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination);
transient private final BooleanProperty useAnimationsProperty = new SimpleBooleanProperty(useAnimations); transient private final BooleanProperty useAnimationsProperty = new SimpleBooleanProperty(useAnimations);
transient private final BooleanProperty useInvertedMarketPriceProperty = new SimpleBooleanProperty(useInvertedMarketPrice);
transient private final ObservableList<FiatCurrency> fiatCurrenciesAsObservable = FXCollections.observableArrayList(); transient private final ObservableList<FiatCurrency> fiatCurrenciesAsObservable = FXCollections.observableArrayList();
transient private final ObservableList<CryptoCurrency> cryptoCurrenciesAsObservable = FXCollections.observableArrayList(); transient private final ObservableList<CryptoCurrency> cryptoCurrenciesAsObservable = FXCollections.observableArrayList();
transient private final ObservableList<TradeCurrency> tradeCurrenciesAsObservable = FXCollections.observableArrayList(); transient private final ObservableList<TradeCurrency> tradeCurrenciesAsObservable = FXCollections.observableArrayList();
@ -130,6 +132,7 @@ public final class Preferences implements Persistable {
if (persisted != null) { if (persisted != null) {
setBtcDenomination(persisted.btcDenomination); setBtcDenomination(persisted.btcDenomination);
setUseAnimations(persisted.useAnimations); setUseAnimations(persisted.useAnimations);
setUseInvertedMarketPrice(persisted.useInvertedMarketPrice);
setFiatCurrencies(persisted.fiatCurrencies); setFiatCurrencies(persisted.fiatCurrencies);
fiatCurrencies = new ArrayList<>(fiatCurrenciesAsObservable); fiatCurrencies = new ArrayList<>(fiatCurrenciesAsObservable);
@ -194,7 +197,11 @@ public final class Preferences implements Persistable {
}); });
useAnimationsProperty.addListener((ov) -> { useAnimationsProperty.addListener((ov) -> {
useAnimations = useAnimationsProperty.get(); useAnimations = useAnimationsProperty.get();
_useAnimations = useAnimations; staticUseAnimations = useAnimations;
storage.queueUpForSave(2000);
});
useInvertedMarketPriceProperty.addListener((ov) -> {
useInvertedMarketPrice = useInvertedMarketPriceProperty.get();
storage.queueUpForSave(2000); storage.queueUpForSave(2000);
}); });
fiatCurrenciesAsObservable.addListener((Observable ov) -> { fiatCurrenciesAsObservable.addListener((Observable ov) -> {
@ -229,12 +236,16 @@ public final class Preferences implements Persistable {
// Setter // Setter
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void setBtcDenomination(String btcDenominationProperty) { public void setBtcDenomination(String btcDenomination) {
this.btcDenominationProperty.set(btcDenominationProperty); this.btcDenominationProperty.set(btcDenomination);
} }
public void setUseAnimations(boolean useAnimationsProperty) { public void setUseAnimations(boolean useAnimations) {
this.useAnimationsProperty.set(useAnimationsProperty); this.useAnimationsProperty.set(useAnimations);
}
public void setUseInvertedMarketPrice(boolean useInvertedMarketPrice) {
this.useInvertedMarketPriceProperty.set(useInvertedMarketPrice);
} }
public void setBitcoinNetwork(BitcoinNetwork bitcoinNetwork) { public void setBitcoinNetwork(BitcoinNetwork bitcoinNetwork) {
@ -343,6 +354,11 @@ public final class Preferences implements Persistable {
storage.queueUpForSave(); storage.queueUpForSave();
} }
public boolean flipUseInvertedMarketPrice() {
setUseInvertedMarketPrice(!getUseInvertedMarketPrice());
return getUseInvertedMarketPrice();
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getter // Getter
@ -352,22 +368,31 @@ public final class Preferences implements Persistable {
return btcDenominationProperty.get(); return btcDenominationProperty.get();
} }
public boolean getUseAnimations() {
return useAnimationsProperty.get();
}
public static boolean useAnimations() {
return _useAnimations;
}
public StringProperty btcDenominationProperty() { public StringProperty btcDenominationProperty() {
return btcDenominationProperty; return btcDenominationProperty;
} }
public boolean getUseAnimations() {
return useAnimationsProperty.get();
}
public BooleanProperty useAnimationsProperty() { public BooleanProperty useAnimationsProperty() {
return useAnimationsProperty; return useAnimationsProperty;
} }
public static boolean useAnimations() {
return staticUseAnimations;
}
public boolean getUseInvertedMarketPrice() {
return useInvertedMarketPriceProperty.get();
}
public BooleanProperty useInvertedMarketPriceProperty() {
return useInvertedMarketPriceProperty;
}
public BitcoinNetwork getBitcoinNetwork() { public BitcoinNetwork getBitcoinNetwork() {
return bitcoinNetwork; return bitcoinNetwork;
} }

View file

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

View file

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

View file

@ -17,6 +17,8 @@
package io.bitsquare.gui.main; package io.bitsquare.gui.main;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.BitsquareException; import io.bitsquare.BitsquareException;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.pricefeed.PriceFeed; 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.portfolio.PortfolioView;
import io.bitsquare.gui.main.settings.SettingsView; import io.bitsquare.gui.main.settings.SettingsView;
import io.bitsquare.gui.util.Transitions; import io.bitsquare.gui.util.Transitions;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
@ -47,8 +47,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment; import javafx.scene.text.TextAlignment;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.monadic.MonadicBinding;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -61,7 +59,6 @@ import static javafx.scene.layout.AnchorPane.*;
public class MainView extends InitializableView<StackPane, MainViewModel> { public class MainView extends InitializableView<StackPane, MainViewModel> {
public static final String TITLE_KEY = "view.title"; public static final String TITLE_KEY = "view.title";
private MonadicBinding<String> marketPriceBinding;
public static StackPane getRootContainer() { public static StackPane getRootContainer() {
return MainView.rootContainer; return MainView.rootContainer;
@ -92,7 +89,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private final ViewLoader viewLoader; private final ViewLoader viewLoader;
private final Navigation navigation; private final Navigation navigation;
private static Transitions transitions; private static Transitions transitions;
private final String title;
private ChangeListener<String> walletServiceErrorMsgListener; private ChangeListener<String> walletServiceErrorMsgListener;
private ChangeListener<String> btcSyncIconIdListener; private ChangeListener<String> btcSyncIconIdListener;
private ChangeListener<String> splashP2PNetworkErrorMsgListener; private ChangeListener<String> splashP2PNetworkErrorMsgListener;
@ -106,6 +102,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private BorderPane baseApplicationContainer; private BorderPane baseApplicationContainer;
private Overlay<Popup> p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup; private Overlay<Popup> p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup;
private static StackPane rootContainer; private static StackPane rootContainer;
private ChangeListener<PriceFeedComboBoxItem> selectedPriceFeedItemListender;
@Inject @Inject
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, Transitions transitions, 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.viewLoader = viewLoader;
this.navigation = navigation; this.navigation = navigation;
MainView.transitions = transitions; MainView.transitions = transitions;
this.title = title;
} }
@Override @Override
@ -138,22 +134,19 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
setTopAnchor(this, 0d); setTopAnchor(this, 0d);
}}; }};
Tuple3<TextField, Label, VBox> marketPriceBox = getMarketPriceBox("Market price"); Tuple3<ComboBox<PriceFeedComboBoxItem>, Label, VBox> marketPriceBox = getMarketPriceBox("Market price");
final BooleanProperty priceInverted = new SimpleBooleanProperty(false); ComboBox<PriceFeedComboBoxItem> priceComboBox = marketPriceBox.first;
marketPriceBox.first.setOnMouseClicked(e -> priceInverted.setValue(!priceInverted.get()));
marketPriceBinding = EasyBind.combine( priceComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
model.marketPriceCurrency, model.marketPrice, model.marketPriceInverted, priceInverted, model.setPriceFeedComboBoxItem(newValue);
(marketPriceCurrency, marketPrice, marketPriceInverted, inverted) ->
(priceInverted.get() ?
marketPriceInverted :
marketPrice) +
(priceInverted.get() ?
" BTC/" + marketPriceCurrency :
" " + marketPriceCurrency + "/BTC"));
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( marketPriceBox.second.textProperty().bind(createStringBinding(
() -> { () -> {
@ -268,22 +261,56 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
return new Tuple2(textField, vBox); return new Tuple2(textField, vBox);
} }
private Tuple3<TextField, Label, VBox> getMarketPriceBox(String text) { private ListCell<PriceFeedComboBoxItem> getPriceFeedComboBoxListCell() {
TextField textField = new TextField(); return new ListCell<PriceFeedComboBoxItem>() {
textField.setEditable(false); @Override
textField.setPrefWidth(180); protected void updateItem(PriceFeedComboBoxItem item, boolean empty) {
textField.setFocusTraversable(false); super.updateItem(item, empty);
textField.setId("price-feed-text-field"); 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 label = new Label(text);
label.setId("nav-balance-label"); label.setId("nav-balance-label");
label.setPadding(new Insets(0, 5, 0, 5)); label.setTextAlignment(TextAlignment.CENTER);
label.setPrefWidth(textField.getPrefWidth()); label.setPadding(new Insets(0, 25, 0, 5));
label.prefWidthProperty().bind(hBox.widthProperty());
VBox vBox = new VBox(); VBox vBox = new VBox();
vBox.setSpacing(3); vBox.setSpacing(3);
vBox.setPadding(new Insets(11, 0, 0, 0)); vBox.setPadding(new Insets(11, 0, 0, 0));
vBox.getChildren().addAll(textField, label); vBox.getChildren().addAll(hBox, label);
return new Tuple3(textField, label, vBox); return new Tuple3(priceComboBox, label, vBox);
} }
public void setPersistedFilesCorrupted(List<String> persistedFilesCorrupted) { 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.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.MarketPrice;
import io.bitsquare.btc.pricefeed.PriceFeed; import io.bitsquare.btc.pricefeed.PriceFeed;
import io.bitsquare.common.Clock; import io.bitsquare.common.Clock;
import io.bitsquare.common.Timer; 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.main.overlays.windows.WalletPasswordWindow;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.P2PServiceListener; import io.bitsquare.p2p.P2PServiceListener;
import io.bitsquare.p2p.network.CloseConnectionReason; 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.payment.OKPayAccount;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager; 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.OpenOffer;
import io.bitsquare.trade.offer.OpenOfferManager; import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.beans.property.*; import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
@ -76,10 +78,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Date; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -94,7 +93,7 @@ public class MainViewModel implements ViewModel {
private final TradeManager tradeManager; private final TradeManager tradeManager;
private final OpenOfferManager openOfferManager; private final OpenOfferManager openOfferManager;
private final DisputeManager disputeManager; private final DisputeManager disputeManager;
private final Preferences preferences; final Preferences preferences;
private final AlertManager alertManager; private final AlertManager alertManager;
private final WalletPasswordWindow walletPasswordWindow; private final WalletPasswordWindow walletPasswordWindow;
private final NotificationCenter notificationCenter; private final NotificationCenter notificationCenter;
@ -113,6 +112,7 @@ public class MainViewModel implements ViewModel {
final StringProperty marketPriceInverted = new SimpleStringProperty("N/A"); final StringProperty marketPriceInverted = new SimpleStringProperty("N/A");
final StringProperty marketPriceCurrency = new SimpleStringProperty(""); final StringProperty marketPriceCurrency = new SimpleStringProperty("");
final ObjectProperty<PriceFeed.Type> typeProperty = new SimpleObjectProperty<>(PriceFeed.Type.LAST); final ObjectProperty<PriceFeed.Type> typeProperty = new SimpleObjectProperty<>(PriceFeed.Type.LAST);
final ObjectProperty<PriceFeedComboBoxItem> selectedPriceFeedComboBoxItemProperty = new SimpleObjectProperty<>();
final StringProperty availableBalance = new SimpleStringProperty(); final StringProperty availableBalance = new SimpleStringProperty();
final StringProperty reservedBalance = new SimpleStringProperty(); final StringProperty reservedBalance = new SimpleStringProperty();
final StringProperty lockedBalance = new SimpleStringProperty(); final StringProperty lockedBalance = new SimpleStringProperty();
@ -139,15 +139,16 @@ public class MainViewModel implements ViewModel {
final StringProperty p2pNetworkLabelId = new SimpleStringProperty("footer-pane"); final StringProperty p2pNetworkLabelId = new SimpleStringProperty("footer-pane");
private MonadicBinding<Boolean> allServicesDone, tradesAndUIReady; private MonadicBinding<Boolean> allServicesDone, tradesAndUIReady;
private final PriceFeed priceFeed; final PriceFeed priceFeed;
private final ClosedTradableManager closedTradableManager;
private final FailedTradesManager failedTradesManager;
private final User user; private final User user;
private int numBtcPeers = 0; private int numBtcPeers = 0;
private Timer checkNumberOfBtcPeersTimer; private Timer checkNumberOfBtcPeersTimer;
private Timer checkNumberOfP2pNetworkPeersTimer; private Timer checkNumberOfP2pNetworkPeersTimer;
private Timer startupTimeout; private Timer startupTimeout;
private final Map<String, Subscription> disputeIsClosedSubscriptionsMap = new HashMap<>(); 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, public MainViewModel(WalletService walletService, TradeWalletService tradeWalletService,
PriceFeed priceFeed, PriceFeed priceFeed,
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager, ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
OpenOfferManager openOfferManager, ClosedTradableManager closedTradableManager, OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
FailedTradesManager failedTradesManager, DisputeManager disputeManager, Preferences preferences,
User user, AlertManager alertManager, WalletPasswordWindow walletPasswordWindow, User user, AlertManager alertManager, WalletPasswordWindow walletPasswordWindow,
NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock, NotificationCenter notificationCenter, TacWindow tacWindow, Clock clock,
KeyRing keyRing, Navigation navigation, BSFormatter formatter) { KeyRing keyRing, Navigation navigation, BSFormatter formatter) {
this.priceFeed = priceFeed; this.priceFeed = priceFeed;
this.closedTradableManager = closedTradableManager;
this.failedTradesManager = failedTradesManager;
this.user = user; this.user = user;
this.walletService = walletService; this.walletService = walletService;
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
@ -472,6 +470,7 @@ public class MainViewModel implements ViewModel {
setupDevDummyPaymentAccount(); setupDevDummyPaymentAccount();
setupMarketPriceFeed(); setupMarketPriceFeed();
swapPendingOfferFundingEntries(); swapPendingOfferFundingEntries();
fillPriceFeedComboBoxItems();
showAppScreen.set(true); showAppScreen.set(true);
@ -666,7 +665,7 @@ public class MainViewModel implements ViewModel {
priceFeed.setType(PriceFeed.Type.LAST); priceFeed.setType(PriceFeed.Type.LAST);
priceFeed.init(price -> { priceFeed.init(price -> {
marketPrice.set(formatter.formatMarketPrice(price)); marketPrice.set(formatter.formatMarketPrice(price));
marketPriceInverted.set(formatter.formatMarketPrice(1 / price, 8)); marketPriceInverted.set(price != 0 ? formatter.formatMarketPrice(1 / price, 8) : "");
}, },
(errorMessage, throwable) -> { (errorMessage, throwable) -> {
marketPrice.set("N/A"); marketPrice.set("N/A");
@ -674,6 +673,74 @@ public class MainViewModel implements ViewModel {
}); });
marketPriceCurrency.bind(priceFeed.currencyCodeProperty()); marketPriceCurrency.bind(priceFeed.currencyCodeProperty());
typeProperty.bind(priceFeed.typeProperty()); 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) { 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); offerBookListItems.addListener(listChangeListener);
offerBook.fillOfferBookListItems(); offerBook.fillOfferBookListItems();
updateChartData(offerBookListItems); updateChartData(offerBookListItems);
priceFeed.setCurrencyCode(tradeCurrency.get().getCode()); //priceFeed.setCurrencyCode(tradeCurrency.get().getCode());
} }
@Override @Override
@ -136,7 +136,7 @@ class MarketsChartsViewModel extends ActivatableViewModel {
public void onSetTradeCurrency(TradeCurrency tradeCurrency) { public void onSetTradeCurrency(TradeCurrency tradeCurrency) {
this.tradeCurrency.set(tradeCurrency); this.tradeCurrency.set(tradeCurrency);
updateChartData(offerBookListItems); updateChartData(offerBookListItems);
priceFeed.setCurrencyCode(tradeCurrency.getCode()); //priceFeed.setCurrencyCode(tradeCurrency.getCode());
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

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

View file

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

View file

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