From b89320f9b77c28d662ec3a7b52b8d3c03e17b5ca Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 6 Apr 2018 10:45:13 +0200 Subject: [PATCH] Add small peer icon (trader icon) to each offer in offer book to emphasize that you are trading with real people on Bisq --- .../bisq/desktop/components/PeerInfoIcon.java | 38 +++++++++----- .../desktop/components/PeerInfoIconSmall.java | 33 ++++++++++++ .../market/offerbook/OfferBookChartView.java | 52 ++++++++++++++++++- .../offerbook/OfferBookChartViewModel.java | 5 +- .../OfferBookChartViewModelTest.java | 20 +++---- 5 files changed, 123 insertions(+), 25 deletions(-) create mode 100644 src/main/java/bisq/desktop/components/PeerInfoIconSmall.java diff --git a/src/main/java/bisq/desktop/components/PeerInfoIcon.java b/src/main/java/bisq/desktop/components/PeerInfoIcon.java index 2e5c150e83..cc7b116bfd 100644 --- a/src/main/java/bisq/desktop/components/PeerInfoIcon.java +++ b/src/main/java/bisq/desktop/components/PeerInfoIcon.java @@ -55,9 +55,10 @@ public class PeerInfoIcon extends Group { private final Map peerTagMap; private final Label numTradesLabel; private final Label tagLabel; - private final Pane tagPane; - private final Pane numTradesPane; + protected final Pane tagPane; + protected final Pane numTradesPane; private final String hostName; + private final double scaleFactor; public PeerInfoIcon(NodeAddress nodeAddress, String role, @@ -70,6 +71,7 @@ public class PeerInfoIcon extends Group { boolean useDevPrivilegeKeys) { this.numTrades = numTrades; + scaleFactor = getScaleFactor(); hostName = nodeAddress != null ? nodeAddress.getHostName() : ""; String address = nodeAddress != null ? nodeAddress.getFullAddress() : ""; @@ -108,12 +110,12 @@ public class PeerInfoIcon extends Group { ringColor = Color.rgb(0, 225, 0); } - double outerSize = 26; + double outerSize = 26 * scaleFactor; Canvas outerBackground = new Canvas(outerSize, outerSize); GraphicsContext outerBackgroundGc = outerBackground.getGraphicsContext2D(); outerBackgroundGc.setFill(ringColor); outerBackgroundGc.fillOval(0, 0, outerSize, outerSize); - outerBackground.setLayoutY(1); + outerBackground.setLayoutY(1 * scaleFactor); // inner circle int maxIndices = 15; @@ -138,36 +140,38 @@ public class PeerInfoIcon extends Group { Color innerColor = Color.rgb(red, green, blue); innerColor = innerColor.deriveColor(1, saturation, 0.8, 1); // reduce saturation and brightness - double innerSize = 22; + double innerSize = scaleFactor * 22; Canvas innerBackground = new Canvas(innerSize, innerSize); GraphicsContext innerBackgroundGc = innerBackground.getGraphicsContext2D(); innerBackgroundGc.setFill(innerColor); innerBackgroundGc.fillOval(0, 0, innerSize, innerSize); - innerBackground.setLayoutY(3); - innerBackground.setLayoutX(2); + innerBackground.setLayoutY(3 * scaleFactor); + innerBackground.setLayoutX(2 * scaleFactor); ImageView avatarImageView = new ImageView(); avatarImageView.setId("avatar_" + index); avatarImageView.setLayoutX(0); - avatarImageView.setLayoutY(1); + avatarImageView.setLayoutY(1 * scaleFactor); + avatarImageView.setFitHeight(scaleFactor * 26); + avatarImageView.setFitWidth(scaleFactor * 26); numTradesPane = new Pane(); - numTradesPane.relocate(18, 14); + numTradesPane.relocate(scaleFactor * 18, scaleFactor * 14); numTradesPane.setMouseTransparent(true); ImageView numTradesCircle = new ImageView(); numTradesCircle.setId("image-green_circle"); numTradesLabel = new AutoTooltipLabel(); - numTradesLabel.relocate(5, 1); + numTradesLabel.relocate(scaleFactor * 5, scaleFactor * 1); numTradesLabel.setId("ident-num-label"); numTradesPane.getChildren().addAll(numTradesCircle, numTradesLabel); tagPane = new Pane(); - tagPane.relocate(18, -2); + tagPane.relocate(Math.round(scaleFactor * 18), scaleFactor * -2); tagPane.setMouseTransparent(true); ImageView tagCircle = new ImageView(); tagCircle.setId("image-blue_circle"); tagLabel = new AutoTooltipLabel(); - tagLabel.relocate(5, 1); + tagLabel.relocate(Math.round(scaleFactor * 5), scaleFactor * 1); tagLabel.setId("ident-num-label"); tagPane.getChildren().addAll(tagCircle, tagLabel); @@ -175,6 +179,10 @@ public class PeerInfoIcon extends Group { getChildren().addAll(outerBackground, innerBackground, avatarImageView, tagPane, numTradesPane); + addMouseListener(numTrades, privateNotificationManager, offer, preferences, formatter, useDevPrivilegeKeys, isFiatCurrency, makersAccountAge); + } + + protected void addMouseListener(int numTrades, PrivateNotificationManager privateNotificationManager, Offer offer, Preferences preferences, BSFormatter formatter, boolean useDevPrivilegeKeys, boolean isFiatCurrency, long makersAccountAge) { final String accountAgeTagEditor = isFiatCurrency ? makersAccountAge > -1 ? formatter.formatAccountAge(makersAccountAge) : @@ -192,7 +200,11 @@ public class PeerInfoIcon extends Group { .show()); } - private void updatePeerInfoIcon() { + protected double getScaleFactor() { + return 1; + } + + protected void updatePeerInfoIcon() { String tag; if (peerTagMap.containsKey(hostName)) { tag = peerTagMap.get(hostName); diff --git a/src/main/java/bisq/desktop/components/PeerInfoIconSmall.java b/src/main/java/bisq/desktop/components/PeerInfoIconSmall.java new file mode 100644 index 0000000000..39095ff5fc --- /dev/null +++ b/src/main/java/bisq/desktop/components/PeerInfoIconSmall.java @@ -0,0 +1,33 @@ +package bisq.desktop.components; + +import bisq.desktop.util.BSFormatter; + +import bisq.core.alert.PrivateNotificationManager; +import bisq.core.offer.Offer; +import bisq.core.payment.AccountAgeWitnessService; +import bisq.core.user.Preferences; + +import bisq.network.p2p.NodeAddress; + +public class PeerInfoIconSmall extends PeerInfoIcon { + public PeerInfoIconSmall(NodeAddress nodeAddress, String role, Offer offer, Preferences preferences, AccountAgeWitnessService accountAgeWitnessService, BSFormatter formatter, boolean useDevPrivilegeKeys) { + // We don't want to show number of trades in that case as it would be unreadable. + // Also we don't need the privateNotificationManager as no interaction will take place with this icon. + super(nodeAddress, role, 0, null, offer, preferences, accountAgeWitnessService, formatter, useDevPrivilegeKeys); + } + + @Override + protected double getScaleFactor() { + return 0.6; + } + + @Override + protected void addMouseListener(int numTrades, PrivateNotificationManager privateNotificationManager, Offer offer, Preferences preferences, BSFormatter formatter, boolean useDevPrivilegeKeys, boolean isFiatCurrency, long makersAccountAge) { + } + + @Override + protected void updatePeerInfoIcon() { + numTradesPane.setVisible(false); + tagPane.setVisible(false); + } +} diff --git a/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java b/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java index 30088a5327..daa7586793 100644 --- a/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java +++ b/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java @@ -24,6 +24,8 @@ import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.ColoredDecimalPlacesWithZerosText; +import bisq.desktop.components.PeerInfoIcon; +import bisq.desktop.components.PeerInfoIconSmall; import bisq.desktop.main.MainView; import bisq.desktop.main.offer.BuyOfferView; import bisq.desktop.main.offer.SellOfferView; @@ -32,15 +34,19 @@ import bisq.desktop.util.BSFormatter; import bisq.desktop.util.CurrencyListItem; import bisq.desktop.util.GUIUtil; +import bisq.core.app.AppOptionKeys; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; +import bisq.network.p2p.NodeAddress; + import bisq.common.UserThread; import bisq.common.util.Tuple4; import javax.inject.Inject; +import javax.inject.Named; import javafx.scene.chart.AreaChart; import javafx.scene.chart.NumberAxis; @@ -90,6 +96,8 @@ import static bisq.desktop.util.Layout.INITIAL_SCENE_HEIGHT; public class OfferBookChartView extends ActivatableViewAndModel { private static final Logger log = LoggerFactory.getLogger(OfferBookChartView.class); + private final boolean useDevPrivilegeKeys; + private NumberAxis xAxis; private XYChart.Series seriesBuy, seriesSell; private final Navigation navigation; @@ -124,10 +132,12 @@ public class OfferBookChartView extends ActivatableViewAndModel avatarColumn = new AutoTooltipTableColumn(Res.get("offerbook.trader")) { + { + setMinWidth(80); + setMaxWidth(80); + setSortable(true); + } + }; + avatarColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + avatarColumn.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + @Override + public void updateItem(final OfferListItem newItem, boolean empty) { + super.updateItem(newItem, empty); + if (newItem != null && !empty) { + final Offer offer = newItem.offer; + final NodeAddress makersNodeAddress = offer.getOwnerNodeAddress(); + String role = Res.get("peerInfoIcon.tooltip.maker"); + PeerInfoIconSmall peerInfoIcon = new PeerInfoIconSmall(makersNodeAddress, + role, + offer, + model.preferences, + model.accountAgeWitnessService, + formatter, + useDevPrivilegeKeys); + setGraphic(peerInfoIcon); + } else { + setGraphic(null); + } + } + }; + } + }); + tableView.getColumns().add(volumeColumn); tableView.getColumns().add(amountColumn); tableView.getColumns().add(priceColumn); + tableView.getColumns().add(avatarColumn); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); Label placeholder = new AutoTooltipLabel(Res.get("table.placeholder.noItems", Res.get("shared.multipleOffers"))); diff --git a/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModel.java b/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModel.java index 642af962fc..d4afa48c72 100644 --- a/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModel.java +++ b/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModel.java @@ -35,6 +35,7 @@ import bisq.core.locale.TradeCurrency; import bisq.core.monetary.Price; import bisq.core.offer.Offer; import bisq.core.offer.OfferPayload; +import bisq.core.payment.AccountAgeWitnessService; import bisq.core.provider.price.PriceFeedService; import bisq.core.user.Preferences; @@ -67,6 +68,7 @@ class OfferBookChartViewModel extends ActivatableViewModel { private final OfferBook offerBook; final Preferences preferences; final PriceFeedService priceFeedService; + final AccountAgeWitnessService accountAgeWitnessService; private final Navigation navigation; final ObjectProperty selectedTradeCurrencyProperty = new SimpleObjectProperty<>(); @@ -92,12 +94,13 @@ class OfferBookChartViewModel extends ActivatableViewModel { @SuppressWarnings("WeakerAccess") @Inject public OfferBookChartViewModel(OfferBook offerBook, Preferences preferences, PriceFeedService priceFeedService, - Navigation navigation, BSFormatter formatter) { + AccountAgeWitnessService accountAgeWitnessService, Navigation navigation, BSFormatter formatter) { this.offerBook = offerBook; this.preferences = preferences; this.priceFeedService = priceFeedService; this.navigation = navigation; this.formatter = formatter; + this.accountAgeWitnessService = accountAgeWitnessService; Optional tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(preferences.getOfferBookChartScreenCurrencyCode()); if (tradeCurrencyOptional.isPresent()) diff --git a/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java b/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java index df00fbf126..c2c04b907a 100644 --- a/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java +++ b/src/test/java/bisq/desktop/main/market/offerbook/OfferBookChartViewModelTest.java @@ -64,7 +64,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, null, new BSFormatter()); assertEquals(0, model.maxPlacesForBuyPrice.intValue()); } @@ -83,7 +83,7 @@ public class OfferBookChartViewModelTest { when(priceFeedService.updateCounterProperty()).thenReturn(new SimpleIntegerProperty()); when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, priceFeedService, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, priceFeedService, null, null, new BSFormatter()); model.activate(); assertEquals(0, model.maxPlacesForBuyPrice.intValue()); } @@ -97,7 +97,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, null, new BSFormatter()); model.activate(); assertEquals(7, model.maxPlacesForBuyPrice.intValue()); offerBookListItems.addAll(make(btcItem.but(with(OfferBookListItemMaker.price, 94016475L)))); @@ -113,7 +113,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, null, new BSFormatter()); assertEquals(0, model.maxPlacesForBuyVolume.intValue()); } @@ -126,7 +126,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, null, new BSFormatter()); model.activate(); assertEquals(4, model.maxPlacesForBuyVolume.intValue()); //0.01 offerBookListItems.addAll(make(btcItem.but(with(OfferBookListItemMaker.amount, 100000000L)))); @@ -142,7 +142,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, null, new BSFormatter()); assertEquals(0, model.maxPlacesForSellPrice.intValue()); } @@ -161,7 +161,7 @@ public class OfferBookChartViewModelTest { when(priceFeedService.updateCounterProperty()).thenReturn(new SimpleIntegerProperty()); when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, priceFeedService, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, priceFeedService, null, null, new BSFormatter()); model.activate(); assertEquals(0, model.maxPlacesForSellPrice.intValue()); } @@ -175,7 +175,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, null, new BSFormatter()); model.activate(); assertEquals(7, model.maxPlacesForSellPrice.intValue()); offerBookListItems.addAll(make(btcSellItem.but(with(OfferBookListItemMaker.price, 94016475L)))); @@ -191,7 +191,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, null, null, null, new BSFormatter()); assertEquals(0, model.maxPlacesForSellVolume.intValue()); } @@ -204,7 +204,7 @@ public class OfferBookChartViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, new BSFormatter()); + final OfferBookChartViewModel model = new OfferBookChartViewModel(offerBook, empty, service, null, null, new BSFormatter()); model.activate(); assertEquals(4, model.maxPlacesForSellVolume.intValue()); //0.01 offerBookListItems.addAll(make(btcSellItem.but(with(OfferBookListItemMaker.amount, 100000000L))));