Show Identicon for offerers or trading peers onion address and indicate repeated trades, Display additional info for Altcoins at buy/sell buttons

This commit is contained in:
Manfred Karrer 2016-03-17 17:58:37 +01:00
parent a3aaee811d
commit e6e8fc56fc
39 changed files with 303 additions and 25 deletions

View file

@ -34,7 +34,6 @@
-fx-image: url("../../../images/buy_white.png"); -fx-image: url("../../../images/buy_white.png");
} }
#image-sell { #image-sell {
-fx-image: url("../../../images/sell.png"); -fx-image: url("../../../images/sell.png");
} }
@ -213,3 +212,64 @@
-fx-image: url("../../../images/link.png"); -fx-image: url("../../../images/link.png");
} }
#avatar_1 {
-fx-image: url("../../../images/avatars/avatar_1.png");
}
#avatar_2 {
-fx-image: url("../../../images/avatars/avatar_2.png");
}
#avatar_3 {
-fx-image: url("../../../images/avatars/avatar_3.png");
}
#avatar_4 {
-fx-image: url("../../../images/avatars/avatar_4.png");
}
#avatar_5 {
-fx-image: url("../../../images/avatars/avatar_5.png");
}
#avatar_6 {
-fx-image: url("../../../images/avatars/avatar_6.png");
}
#avatar_7 {
-fx-image: url("../../../images/avatars/avatar_7.png");
}
#avatar_8 {
-fx-image: url("../../../images/avatars/avatar_8.png");
}
#avatar_9 {
-fx-image: url("../../../images/avatars/avatar_9.png");
}
#avatar_10 {
-fx-image: url("../../../images/avatars/avatar_10.png");
}
#avatar_11 {
-fx-image: url("../../../images/avatars/avatar_11.png");
}
#avatar_12 {
-fx-image: url("../../../images/avatars/avatar_12.png");
}
#avatar_13 {
-fx-image: url("../../../images/avatars/avatar_13.png");
}
#avatar_14 {
-fx-image: url("../../../images/avatars/avatar_14.png");
}
#avatar_15 {
-fx-image: url("../../../images/avatars/avatar_15.png");
}

View file

@ -32,6 +32,7 @@ import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
import io.bitsquare.gui.main.offer.OfferView; import io.bitsquare.gui.main.offer.OfferView;
import io.bitsquare.gui.main.overlays.popups.Popup; import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow; import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.gui.util.Layout; import io.bitsquare.gui.util.Layout;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.CryptoCurrency; import io.bitsquare.locale.CryptoCurrency;
@ -43,6 +44,7 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.geometry.HPos; import javafx.geometry.HPos;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.VPos; import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
@ -153,6 +155,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
paymentMethodColumn = getPaymentMethodColumn(); paymentMethodColumn = getPaymentMethodColumn();
tableView.getColumns().add(paymentMethodColumn); tableView.getColumns().add(paymentMethodColumn);
tableView.getColumns().add(getActionColumn()); tableView.getColumns().add(getActionColumn());
tableView.getColumns().add(getAvatarColumn());
tableView.getSortOrder().add(priceColumn); tableView.getSortOrder().add(priceColumn);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
@ -192,17 +195,18 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem())); paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem()));
createOfferButton.setOnAction(e -> onCreateOffer()); createOfferButton.setOnAction(e -> onCreateOffer());
priceColumn.textProperty().bind(createStringBinding(
() -> !model.showAllTradeCurrenciesProperty.get() ?
"Price in " + model.tradeCurrencyCode.get() + "/BTC" :
"Price",
model.tradeCurrencyCode,
model.showAllTradeCurrenciesProperty));
volumeColumn.textProperty().bind(createStringBinding( volumeColumn.textProperty().bind(createStringBinding(
() -> !model.showAllTradeCurrenciesProperty.get() ? () -> {
"Amount in " + model.tradeCurrencyCode.get() + " (Min.)" : setDirectionTitles();
"Amount (Min.)", String tradeCurrencyCode = model.tradeCurrencyCode.get();
boolean showAllTradeCurrencies = model.showAllTradeCurrenciesProperty.get();
priceColumn.setText(!showAllTradeCurrencies ?
"Price in " + tradeCurrencyCode + "/BTC" :
"Price");
return !showAllTradeCurrencies ?
"Amount in " + tradeCurrencyCode + " (Min.)" :
"Amount (Min.)";
},
model.tradeCurrencyCode, model.tradeCurrencyCode,
model.showAllTradeCurrenciesProperty)); model.showAllTradeCurrenciesProperty));
@ -212,6 +216,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
tableView.setItems(model.getOfferList()); tableView.setItems(model.getOfferList());
priceColumn.setSortType((model.getDirection() == Offer.Direction.BUY) ? TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING); priceColumn.setSortType((model.getDirection() == Offer.Direction.BUY) ? TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
tableView.sort(); tableView.sort();
} }
@Override @Override
@ -237,17 +242,22 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
ImageView iconView = new ImageView(); ImageView iconView = new ImageView();
createOfferButton.setGraphic(iconView); createOfferButton.setGraphic(iconView);
if (direction == Offer.Direction.SELL) { iconView.setId(direction == Offer.Direction.SELL ? "image-sell-white" : "image-buy-white");
offerBookTitle.setText("Offers for buying bitcoin "); createOfferButton.setId(direction == Offer.Direction.SELL ? "sell-button-big" : "buy-button-big");
createOfferButton.setId("sell-button-big");
createOfferButton.setText("Create new offer for selling bitcoin"); setDirectionTitles();
iconView.setId("image-sell-white"); }
} else {
offerBookTitle.setText("Offers for selling bitcoin "); private void setDirectionTitles() {
createOfferButton.setId("buy-button-big"); Offer.Direction direction = model.getDirection();
createOfferButton.setText("Create new offer for buying bitcoin"); String directionText = direction == Offer.Direction.BUY ? "buying" : "selling";
iconView.setId("image-buy-white"); String mirroredDirectionText = direction == Offer.Direction.SELL ? "buying" : "selling";
} TradeCurrency selectedTradeCurrency = model.getSelectedTradeCurrency();
String postFix = selectedTradeCurrency instanceof FiatCurrency || model.showAllTradeCurrenciesProperty.get() ? "" :
" (" + mirroredDirectionText + " " + selectedTradeCurrency.getName() + ")";
offerBookTitle.setText("Offers for " + directionText + " bitcoin" + postFix);
createOfferButton.setText("Create new offer for " + directionText + " bitcoin" + postFix);
} }
public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) { public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) {
@ -568,5 +578,44 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
}); });
return column; return column;
} }
private TableColumn<OfferBookListItem, OfferBookListItem> getAvatarColumn() {
TableColumn<OfferBookListItem, OfferBookListItem> column = new TableColumn<OfferBookListItem, OfferBookListItem>("") {
{
setMinWidth(32);
setMaxWidth(32);
setSortable(true);
}
};
column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
column.setCellFactory(
new Callback<TableColumn<OfferBookListItem, OfferBookListItem>, TableCell<OfferBookListItem,
OfferBookListItem>>() {
@Override
public TableCell<OfferBookListItem, OfferBookListItem> call(TableColumn<OfferBookListItem, OfferBookListItem> column) {
return new TableCell<OfferBookListItem, OfferBookListItem>() {
@Override
public void updateItem(final OfferBookListItem newItem, boolean empty) {
super.updateItem(newItem, empty);
if (newItem != null && !empty) {
String hostName = newItem.getOffer().getOwnerNodeAddress().hostName;
int numPastTrades = model.getNumPastTrades(newItem.getOffer());
boolean hasTraded = numPastTrades > 0;
String tooltipText = hasTraded ? "Offerers onion address: " + hostName + "\n" +
"You have already traded " + numPastTrades + " times with that offerer." : "Offerers onion address: " + hostName;
Node identIcon = ImageUtil.getIdentIcon(hostName, tooltipText, hasTraded);
if (identIcon != null)
setGraphic(identIcon);
} else {
setGraphic(null);
}
}
};
}
});
return column;
}
} }

View file

@ -33,6 +33,8 @@ import io.bitsquare.locale.*;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.*; import io.bitsquare.payment.*;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.trade.offer.OpenOfferManager; import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
@ -69,6 +71,7 @@ class OfferBookViewModel extends ActivatableViewModel {
private final Preferences preferences; private final Preferences preferences;
private final P2PService p2PService; private final P2PService p2PService;
private final PriceFeed priceFeed; private final PriceFeed priceFeed;
private ClosedTradableManager closedTradableManager;
private Navigation navigation; private Navigation navigation;
final BSFormatter formatter; final BSFormatter formatter;
@ -101,6 +104,7 @@ class OfferBookViewModel extends ActivatableViewModel {
@Inject @Inject
public OfferBookViewModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook, public OfferBookViewModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook,
Preferences preferences, P2PService p2PService, PriceFeed priceFeed, Preferences preferences, P2PService p2PService, PriceFeed priceFeed,
ClosedTradableManager closedTradableManager,
Navigation navigation, BSFormatter formatter) { Navigation navigation, BSFormatter formatter) {
super(); super();
@ -110,6 +114,7 @@ class OfferBookViewModel extends ActivatableViewModel {
this.preferences = preferences; this.preferences = preferences;
this.p2PService = p2PService; this.p2PService = p2PService;
this.priceFeed = priceFeed; this.priceFeed = priceFeed;
this.closedTradableManager = closedTradableManager;
this.navigation = navigation; this.navigation = navigation;
this.formatter = formatter; this.formatter = formatter;
@ -435,4 +440,12 @@ class OfferBookViewModel extends ActivatableViewModel {
private boolean isEditEntry(String id) { private boolean isEditEntry(String id) {
return id.equals(EDIT_FLAG); return id.equals(EDIT_FLAG);
} }
public int getNumPastTrades(Offer offer) {
return closedTradableManager.getClosedTrades().stream()
.filter(e -> e instanceof Trade && ((Trade) e).getTradingPeerNodeAddress() != null &&
((Trade) e).getTradingPeerNodeAddress().hostName.equals(offer.getOffererNodeAddress().hostName))
.collect(Collectors.toSet())
.size();
}
} }

View file

@ -35,6 +35,7 @@
<TableColumn text="Trade amount" fx:id="volumeColumn" minWidth="130"/> <TableColumn text="Trade amount" fx:id="volumeColumn" minWidth="130"/>
<TableColumn text="Trade type" fx:id="directionColumn" minWidth="80"/> <TableColumn text="Trade type" fx:id="directionColumn" minWidth="80"/>
<TableColumn text="State" fx:id="stateColumn" minWidth="80" sortable="false"/> <TableColumn text="State" fx:id="stateColumn" minWidth="80" sortable="false"/>
<TableColumn text="" fx:id="avatarColumn" minWidth="32" maxWidth="32"/>
</columns> </columns>
</TableView> </TableView>

View file

@ -23,11 +23,13 @@ import io.bitsquare.gui.components.HyperlinkWithIcon;
import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow; import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow; import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.trade.Tradable; import io.bitsquare.trade.Tradable;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.offer.OpenOffer; import io.bitsquare.trade.offer.OpenOffer;
import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.util.Callback; import javafx.util.Callback;
@ -41,7 +43,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
TableView<ClosedTradableListItem> table; TableView<ClosedTradableListItem> table;
@FXML @FXML
TableColumn<ClosedTradableListItem, ClosedTradableListItem> priceColumn, amountColumn, volumeColumn, TableColumn<ClosedTradableListItem, ClosedTradableListItem> priceColumn, amountColumn, volumeColumn,
directionColumn, dateColumn, tradeIdColumn, stateColumn; directionColumn, dateColumn, tradeIdColumn, stateColumn, avatarColumn;
private final BSFormatter formatter; private final BSFormatter formatter;
private final OfferDetailsWindow offerDetailsWindow; private final OfferDetailsWindow offerDetailsWindow;
private final TradeDetailsWindow tradeDetailsWindow; private final TradeDetailsWindow tradeDetailsWindow;
@ -63,6 +65,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
setVolumeColumnCellFactory(); setVolumeColumnCellFactory();
setDateColumnCellFactory(); setDateColumnCellFactory();
setStateColumnCellFactory(); setStateColumnCellFactory();
setAvatarColumnCellFactory();
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"));
@ -155,6 +158,34 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}); });
} }
private TableColumn<ClosedTradableListItem, ClosedTradableListItem> setAvatarColumnCellFactory() {
avatarColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
avatarColumn.setCellFactory(
new Callback<TableColumn<ClosedTradableListItem, ClosedTradableListItem>, TableCell<ClosedTradableListItem,
ClosedTradableListItem>>() {
@Override
public TableCell<ClosedTradableListItem, ClosedTradableListItem> call(TableColumn<ClosedTradableListItem, ClosedTradableListItem> column) {
return new TableCell<ClosedTradableListItem, ClosedTradableListItem>() {
@Override
public void updateItem(final ClosedTradableListItem newItem, boolean empty) {
super.updateItem(newItem, empty);
if (newItem != null && !empty && newItem.getTradable() instanceof Trade) {
String hostName = ((Trade) newItem.getTradable()).getTradingPeerNodeAddress().hostName;
Node identIcon = ImageUtil.getIdentIcon(hostName, "Trading peers onion address: " + hostName, true);
if (identIcon != null)
setGraphic(identIcon);
} else {
setGraphic(null);
}
}
};
}
});
return avatarColumn;
}
private void setAmountColumnCellFactory() { private void setAmountColumnCellFactory() {
amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));

View file

@ -52,6 +52,7 @@
</TableColumn> </TableColumn>
<TableColumn text="Payment method" fx:id="paymentMethodColumn" minWidth="120"/> <TableColumn text="Payment method" fx:id="paymentMethodColumn" minWidth="120"/>
<TableColumn text="My role" fx:id="roleColumn" minWidth="120" maxWidth="120"/> <TableColumn text="My role" fx:id="roleColumn" minWidth="120" maxWidth="120"/>
<TableColumn text="" fx:id="avatarColumn" minWidth="32" maxWidth="32"/>
</columns> </columns>
</TableView> </TableView>
</VBox> </VBox>

View file

@ -25,9 +25,11 @@ import io.bitsquare.gui.components.HyperlinkWithIcon;
import io.bitsquare.gui.main.overlays.popups.Popup; import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow; import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.ImageUtil;
import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.control.cell.TextFieldTableCell;
@ -56,7 +58,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@FXML @FXML
TableColumn<PendingTradesListItem, Fiat> priceColumn, tradeVolumeColumn; TableColumn<PendingTradesListItem, Fiat> priceColumn, tradeVolumeColumn;
@FXML @FXML
TableColumn<PendingTradesListItem, PendingTradesListItem> roleColumn, paymentMethodColumn, idColumn, dateColumn; TableColumn<PendingTradesListItem, PendingTradesListItem> avatarColumn, roleColumn, paymentMethodColumn, idColumn, dateColumn;
@FXML @FXML
TableColumn<PendingTradesListItem, Coin> tradeAmountColumn; TableColumn<PendingTradesListItem, Coin> tradeAmountColumn;
@ -88,6 +90,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
setVolumeColumnCellFactory(); setVolumeColumnCellFactory();
setPaymentMethodColumnCellFactory(); setPaymentMethodColumnCellFactory();
setRoleColumnCellFactory(); setRoleColumnCellFactory();
setAvatarColumnCellFactory();
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"));
@ -365,5 +368,37 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
} }
}); });
} }
private TableColumn<PendingTradesListItem, PendingTradesListItem> setAvatarColumnCellFactory() {
avatarColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
avatarColumn.setCellFactory(
new Callback<TableColumn<PendingTradesListItem, PendingTradesListItem>, TableCell<PendingTradesListItem,
PendingTradesListItem>>() {
@Override
public TableCell<PendingTradesListItem, PendingTradesListItem> call(TableColumn<PendingTradesListItem, PendingTradesListItem> column) {
return new TableCell<PendingTradesListItem, PendingTradesListItem>() {
@Override
public void updateItem(final PendingTradesListItem newItem, boolean empty) {
super.updateItem(newItem, empty);
if (newItem != null && !empty && newItem.getTrade().getTradingPeerNodeAddress() != null) {
String hostName = newItem.getTrade().getTradingPeerNodeAddress().hostName;
int numPastTrades = model.getNumPastTrades(newItem.getTrade());
boolean hasTraded = numPastTrades > 0;
String tooltipText = hasTraded ? "Trading peers onion address: " + hostName + "\n" +
"You have already traded " + numPastTrades + " times with that peer." : "Trading peers onion address: " + hostName;
Node identIcon = ImageUtil.getIdentIcon(hostName, tooltipText, hasTraded);
if (identIcon != null)
setGraphic(identIcon);
} else {
setGraphic(null);
}
}
};
}
});
return avatarColumn;
}
} }

View file

@ -30,6 +30,7 @@ import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.PaymentMethod; import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.Contract; import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.beans.property.*; import javafx.beans.property.*;
@ -38,6 +39,8 @@ import org.bitcoinj.core.BlockChainListener;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import java.util.stream.Collectors;
import static io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel.SellerState.*; import static io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel.SellerState.*;
public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataModel> implements ViewModel { public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataModel> implements ViewModel {
@ -70,6 +73,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
public final P2PService p2PService; public final P2PService p2PService;
public final User user; public final User user;
private ClosedTradableManager closedTradableManager;
public final Clock clock; public final Clock clock;
private final ObjectProperty<BuyerState> buyerState = new SimpleObjectProperty<>(); private final ObjectProperty<BuyerState> buyerState = new SimpleObjectProperty<>();
@ -88,6 +92,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
BtcAddressValidator btcAddressValidator, BtcAddressValidator btcAddressValidator,
P2PService p2PService, P2PService p2PService,
User user, User user,
ClosedTradableManager closedTradableManager,
Clock clock) { Clock clock) {
super(dataModel); super(dataModel);
@ -95,6 +100,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
this.btcAddressValidator = btcAddressValidator; this.btcAddressValidator = btcAddressValidator;
this.p2PService = p2PService; this.p2PService = p2PService;
this.user = user; this.user = user;
this.closedTradableManager = closedTradableManager;
this.clock = clock; this.clock = clock;
} }
@ -256,6 +262,13 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return dataModel.getOffer() != null && dataModel.getOffer().getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS); return dataModel.getOffer() != null && dataModel.getOffer().getPaymentMethod().equals(PaymentMethod.BLOCK_CHAINS);
} }
public int getNumPastTrades(Trade trade) {
return closedTradableManager.getClosedTrades().stream()
.filter(e -> e instanceof Trade && ((Trade) e).getTradingPeerNodeAddress() != null &&
((Trade) e).getTradingPeerNodeAddress().hostName.equals(trade.getTradingPeerNodeAddress().hostName))
.collect(Collectors.toSet())
.size();
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// States // States

View file

@ -19,11 +19,21 @@ package io.bitsquare.gui.util;
import com.sun.javafx.tk.quantum.QuantumToolkit; import com.sun.javafx.tk.quantum.QuantumToolkit;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
public class ImageUtil { public class ImageUtil {
private static final Logger log = LoggerFactory.getLogger(ImageUtil.class); private static final Logger log = LoggerFactory.getLogger(ImageUtil.class);
@ -55,6 +65,71 @@ public class ImageUtil {
} }
public static boolean isRetina() { public static boolean isRetina() {
return ((QuantumToolkit) QuantumToolkit.getToolkit()).getMaxRenderScale() > 1.9f; float maxRenderScale = ((QuantumToolkit) QuantumToolkit.getToolkit()).getMaxRenderScale();
boolean isRetina = maxRenderScale > 1.9f;
log.info("isRetina=" + isRetina + " / maxRenderScale=" + maxRenderScale);
return isRetina;
}
public static Node getIdentIcon(String hostName, String tooltipText, boolean hasTraded) {
if (!hostName.isEmpty()) {
// for testing locally we use a random hostname to get dif. colors
if (hostName.startsWith("localhost"))
hostName = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
int maxIndices = 15;
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] bytes = md.digest(hostName.getBytes());
int intValue = Math.abs(((bytes[0] & 0xFF) << 24) | ((bytes[1] & 0xFF) << 16)
| ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF));
int index = (intValue % maxIndices) + 1;
int red = intValue % 256;
int green = (intValue >> 8) % 64; // we use green for marking repeated trades, so avoid it in main bg color
int blue = (intValue >> 16) % 256;
ImageView iconView = new ImageView();
iconView.setId("avatar_" + index);
iconView.setScaleX(intValue % 2 == 0 ? 1d : -1d);
double size = 26;
Group iconGroup = new Group();
Color color = Color.rgb(red, green, blue);
color = color.deriveColor(1, 0.6, 1, 1); // reduce saturation
if (hasTraded) {
Canvas outerBg = new Canvas(size, size);
GraphicsContext gc = outerBg.getGraphicsContext2D();
gc.setFill(Color.rgb(0, 170, 51)); // green
gc.fillOval(0, 0, size, size);
outerBg.setLayoutY(1);
Canvas innerBg = new Canvas(size, size);
GraphicsContext gc2 = innerBg.getGraphicsContext2D();
gc2.setFill(color);
gc2.fillOval(2, 2, size - 4, size - 4);
innerBg.setLayoutY(1);
iconGroup.getChildren().addAll(outerBg, innerBg, iconView);
} else {
Canvas bg = new Canvas(size, size);
GraphicsContext gc = bg.getGraphicsContext2D();
gc.setFill(color);
gc.fillOval(0, 0, size, size);
bg.setLayoutY(1);
iconGroup.getChildren().addAll(bg, iconView);
}
Tooltip.install(iconGroup, new Tooltip(tooltipText));
return iconGroup;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
log.error(e.toString());
return null;
}
} else {
return null;
}
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB