Add small peer icon (trader icon) to each offer in offer book to emphasize that you are trading with real people on Bisq

This commit is contained in:
Christoph Atteneder 2018-04-06 10:45:13 +02:00
parent a7bf94cc1e
commit b89320f9b7
No known key found for this signature in database
GPG key ID: CD5DC1C529CDFD3B
5 changed files with 123 additions and 25 deletions

View file

@ -55,9 +55,10 @@ public class PeerInfoIcon extends Group {
private final Map<String, String> 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);

View file

@ -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);
}
}

View file

@ -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<VBox, OfferBookChartViewModel> {
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<VBox, OfferBookC
@SuppressWarnings("WeakerAccess")
@Inject
public OfferBookChartView(OfferBookChartViewModel model, Navigation navigation, BSFormatter formatter) {
public OfferBookChartView(OfferBookChartViewModel model, Navigation navigation, BSFormatter formatter,
@Named(AppOptionKeys.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) {
super(model);
this.navigation = navigation;
this.formatter = formatter;
this.useDevPrivilegeKeys = useDevPrivilegeKeys;
}
@Override
@ -469,9 +479,49 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
}
});
// trader avatar
TableColumn<OfferListItem, OfferListItem> avatarColumn = new AutoTooltipTableColumn<OfferListItem, OfferListItem>(Res.get("offerbook.trader")) {
{
setMinWidth(80);
setMaxWidth(80);
setSortable(true);
}
};
avatarColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
avatarColumn.setCellFactory(
new Callback<TableColumn<OfferListItem, OfferListItem>, TableCell<OfferListItem,
OfferListItem>>() {
@Override
public TableCell<OfferListItem, OfferListItem> call(TableColumn<OfferListItem, OfferListItem> column) {
return new TableCell<OfferListItem, OfferListItem>() {
@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")));

View file

@ -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<TradeCurrency> 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<TradeCurrency> tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(preferences.getOfferBookChartScreenCurrencyCode());
if (tradeCurrencyOptional.isPresent())

View file

@ -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))));