From cc7346544b8cc70d7108587b35c5c9b0eb4d1a6f Mon Sep 17 00:00:00 2001 From: jmacxx <47253594+jmacxx@users.noreply.github.com> Date: Tue, 1 Jun 2021 15:27:58 -0500 Subject: [PATCH 1/2] feature to enable user avatars and tags in Disputes --- .../resources/i18n/displayStrings.properties | 1 + .../bisq/desktop/components/PeerInfoIcon.java | 190 ++--------------- .../components/PeerInfoIconDispute.java | 56 +++++ .../desktop/components/PeerInfoIconSmall.java | 2 +- .../components/PeerInfoIconTrading.java | 199 ++++++++++++++++++ .../main/offer/offerbook/OfferBookView.java | 4 +- .../closedtrades/ClosedTradesView.java | 4 +- .../pendingtrades/PendingTradesView.java | 4 +- .../main/support/dispute/DisputeView.java | 53 ++++- .../java/bisq/network/p2p/NodeAddress.java | 11 + 10 files changed, 342 insertions(+), 182 deletions(-) create mode 100644 desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java create mode 100644 desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index b1c83ed06e..d30053eac0 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -3074,6 +3074,7 @@ peerInfoIcon.tooltip.trade.traded={0} onion address: {1}\nYou have already trade peerInfoIcon.tooltip.trade.notTraded={0} onion address: {1}\nYou have not traded with that peer so far.\n{2} peerInfoIcon.tooltip.age=Payment account created {0} ago. peerInfoIcon.tooltip.unknownAge=Payment account age not known. +peerInfoIcon.tooltip.dispute={0}\nNumber of disputes: {1}.\n{2} tooltip.openPopupForDetails=Open popup for details tooltip.invalidTradeState.warning=This trade is in an invalid state. Open the details window for more information diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java index e400b29692..9566cdce44 100644 --- a/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java @@ -20,23 +20,16 @@ package bisq.desktop.components; import bisq.desktop.main.overlays.editor.PeerInfoWithTagEditor; import bisq.desktop.util.DisplayUtils; -import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.PrivateNotificationManager; -import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.offer.Offer; -import bisq.core.payment.payload.PaymentMethod; import bisq.core.trade.Trade; import bisq.core.user.Preferences; import bisq.network.p2p.NodeAddress; -import bisq.common.util.Tuple5; - import com.google.common.base.Charsets; -import org.apache.commons.lang3.StringUtils; - import javafx.scene.Group; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; @@ -51,132 +44,37 @@ import javafx.geometry.Point2D; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Date; import java.util.Map; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; -import static com.google.common.base.Preconditions.checkNotNull; - @Slf4j public class PeerInfoIcon extends Group { - private final String tooltipText; - private final int numTrades; - private final AccountAgeWitnessService accountAgeWitnessService; - private final Map peerTagMap; - private final Label numTradesLabel; - private final Label tagLabel; - final Pane tagPane; - final Pane numTradesPane; - private final String fullAddress; - - public PeerInfoIcon(NodeAddress nodeAddress, - String role, - int numTrades, - PrivateNotificationManager privateNotificationManager, - Offer offer, - Preferences preferences, - AccountAgeWitnessService accountAgeWitnessService, - boolean useDevPrivilegeKeys) { - this(nodeAddress, - role, - numTrades, - privateNotificationManager, - offer, - null, - preferences, - accountAgeWitnessService, - useDevPrivilegeKeys); - + public interface notify { + void avatarTagUpdated(); } - public PeerInfoIcon(NodeAddress nodeAddress, - String role, - int numTrades, - PrivateNotificationManager privateNotificationManager, - Trade trade, - Preferences preferences, - AccountAgeWitnessService accountAgeWitnessService, - boolean useDevPrivilegeKeys) { - this(nodeAddress, - role, - numTrades, - privateNotificationManager, - trade.getOffer(), - trade, - preferences, - accountAgeWitnessService, - useDevPrivilegeKeys); + @Setter + private notify callback; + protected Preferences preferences; + protected final String fullAddress; + protected String tooltipText; + protected Label tagLabel; + private Label numTradesLabel; + protected Pane tagPane; + protected Pane numTradesPane; + protected int numTrades = 0; + + public PeerInfoIcon(NodeAddress nodeAddress, Preferences preferences) { + this.preferences = preferences; + this.fullAddress = nodeAddress != null ? nodeAddress.getFullAddress() : ""; } - private PeerInfoIcon(NodeAddress nodeAddress, - String role, - int numTrades, - PrivateNotificationManager privateNotificationManager, - @Nullable Offer offer, - @Nullable Trade trade, - Preferences preferences, - AccountAgeWitnessService accountAgeWitnessService, - boolean useDevPrivilegeKeys) { - this.numTrades = numTrades; - this.accountAgeWitnessService = accountAgeWitnessService; - + protected void createAvatar(Color ringColor) { double scaleFactor = getScaleFactor(); - fullAddress = nodeAddress != null ? nodeAddress.getFullAddress() : ""; - - peerTagMap = preferences.getPeerTagMap(); - - boolean hasTraded = numTrades > 0; - Tuple5 peersAccount = getPeersAccountAge(trade, offer); - - Long accountAge = peersAccount.first; - Long signAge = peersAccount.second; - - if (offer == null) { - checkNotNull(trade, "Trade must not be null if offer is null."); - offer = trade.getOffer(); - } - - checkNotNull(offer, "Offer must not be null"); - - boolean isFiatCurrency = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()); - - String accountAgeTooltip = isFiatCurrency ? - accountAge > -1 ? Res.get("peerInfoIcon.tooltip.age", DisplayUtils.formatAccountAge(accountAge)) : - Res.get("peerInfoIcon.tooltip.unknownAge") : - ""; - tooltipText = hasTraded ? - Res.get("peerInfoIcon.tooltip.trade.traded", role, fullAddress, numTrades, accountAgeTooltip) : - Res.get("peerInfoIcon.tooltip.trade.notTraded", role, fullAddress, accountAgeTooltip); - - // outer circle - Color ringColor; - if (isFiatCurrency) { - - switch (accountAgeWitnessService.getPeersAccountAgeCategory(hasChargebackRisk(trade, offer) ? signAge : accountAge)) { - case TWO_MONTHS_OR_MORE: - ringColor = Color.rgb(0, 225, 0); // > 2 months green - break; - case ONE_TO_TWO_MONTHS: - ringColor = Color.rgb(0, 139, 205); // 1-2 months blue - break; - case LESS_ONE_MONTH: - ringColor = Color.rgb(255, 140, 0); //< 1 month orange - break; - case UNVERIFIED: - default: - ringColor = Color.rgb(255, 0, 0); // not signed, red - break; - } - - - } else { - // for altcoins we always display green - ringColor = Color.rgb(0, 225, 0); - } - double outerSize = 26 * scaleFactor; Canvas outerBackground = new Canvas(outerSize, outerSize); GraphicsContext outerBackgroundGc = outerBackground.getGraphicsContext2D(); @@ -245,54 +143,6 @@ public class PeerInfoIcon extends Group { updatePeerInfoIcon(); getChildren().addAll(outerBackground, innerBackground, avatarImageView, tagPane, numTradesPane); - - addMouseListener(numTrades, privateNotificationManager, trade, offer, preferences, useDevPrivilegeKeys, - isFiatCurrency, accountAge, signAge, peersAccount.third, peersAccount.fourth, peersAccount.fifth); - } - - /** - * @param trade Open trade for trading peer info to be shown - * @param offer Open offer for trading peer info to be shown - * @return account age, sign age, account info, sign info, sign state - */ - private Tuple5 getPeersAccountAge(@Nullable Trade trade, - @Nullable Offer offer) { - AccountAgeWitnessService.SignState signState; - long signAge = -1L; - long accountAge = -1L; - - if (trade != null) { - offer = trade.getOffer(); - if (offer == null) { - // unexpected - return new Tuple5<>(signAge, accountAge, Res.get("peerInfo.age.noRisk"), null, null); - } - signState = accountAgeWitnessService.getSignState(trade); - signAge = accountAgeWitnessService.getWitnessSignAge(trade, new Date()); - accountAge = accountAgeWitnessService.getAccountAge(trade); - } else { - checkNotNull(offer, "Offer must not be null if trade is null."); - signState = accountAgeWitnessService.getSignState(offer); - signAge = accountAgeWitnessService.getWitnessSignAge(offer, new Date()); - accountAge = accountAgeWitnessService.getAccountAge(offer); - } - - if (hasChargebackRisk(trade, offer)) { - String signAgeInfo = Res.get("peerInfo.age.chargeBackRisk"); - String accountSigningState = StringUtils.capitalize(signState.getDisplayString()); - if (signState.equals(AccountAgeWitnessService.SignState.UNSIGNED)) - signAgeInfo = null; - - return new Tuple5<>(accountAge, signAge, Res.get("peerInfo.age.noRisk"), signAgeInfo, accountSigningState); - } - return new Tuple5<>(accountAge, signAge, Res.get("peerInfo.age.noRisk"), null, null); - } - - private boolean hasChargebackRisk(@Nullable Trade trade, @Nullable Offer offer) { - Offer offerToCheck = trade != null ? trade.getOffer() : offer; - - return offerToCheck != null && - PaymentMethod.hasChargebackRisk(offerToCheck.getPaymentMethod(), offerToCheck.getCurrencyCode()); } protected void addMouseListener(int numTrades, @@ -332,6 +182,9 @@ public class PeerInfoIcon extends Group { .onSave(newTag -> { preferences.setTagForPeer(fullAddress, newTag); updatePeerInfoIcon(); + if (callback != null) { + callback.avatarTagUpdated(); + } }) .show()); } @@ -342,6 +195,7 @@ public class PeerInfoIcon extends Group { protected void updatePeerInfoIcon() { String tag; + Map peerTagMap = preferences.getPeerTagMap(); if (peerTagMap.containsKey(fullAddress)) { tag = peerTagMap.get(fullAddress); final String text = !tag.isEmpty() ? Res.get("peerInfoIcon.tooltip", tooltipText, tag) : tooltipText; diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java new file mode 100644 index 0000000000..adeeead467 --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java @@ -0,0 +1,56 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.components; + +import bisq.desktop.util.DisplayUtils; + +import bisq.core.locale.Res; +import bisq.core.user.Preferences; + +import bisq.network.p2p.NodeAddress; + +import javafx.scene.paint.Color; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class PeerInfoIconDispute extends PeerInfoIcon { + + public PeerInfoIconDispute(NodeAddress nodeAddress, + String nrOfDisputes, + long accountAge, + Preferences preferences) { + super(nodeAddress, preferences); + + String accountAgeTooltip = accountAge > -1 ? + Res.get("peerInfoIcon.tooltip.age", DisplayUtils.formatAccountAge(accountAge)) : + Res.get("peerInfoIcon.tooltip.unknownAge"); + + tooltipText = Res.get("peerInfoIcon.tooltip.dispute", fullAddress, nrOfDisputes, accountAgeTooltip); + + // outer circle always display gray + Color ringColor = Color.rgb(128, 128, 128); + createAvatar(ringColor); + addMouseListener(numTrades, null, null, null, preferences, false, + false, accountAge, 0L, null, null, null); + } + + public void refreshTag() { + updatePeerInfoIcon(); + } +} diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconSmall.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconSmall.java index cee0bec7fd..4fa2a2b2a6 100644 --- a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconSmall.java +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconSmall.java @@ -10,7 +10,7 @@ import bisq.network.p2p.NodeAddress; import javax.annotation.Nullable; -public class PeerInfoIconSmall extends PeerInfoIcon { +public class PeerInfoIconSmall extends PeerInfoIconTrading { public PeerInfoIconSmall(NodeAddress nodeAddress, String role, Offer offer, diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java new file mode 100644 index 0000000000..20a27b4c3f --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java @@ -0,0 +1,199 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.components; + +import bisq.desktop.util.DisplayUtils; + +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.alert.PrivateNotificationManager; +import bisq.core.locale.CurrencyUtil; +import bisq.core.locale.Res; +import bisq.core.offer.Offer; +import bisq.core.payment.payload.PaymentMethod; +import bisq.core.trade.Trade; +import bisq.core.user.Preferences; + +import bisq.network.p2p.NodeAddress; + +import bisq.common.util.Tuple5; + +import org.apache.commons.lang3.StringUtils; + +import javafx.scene.paint.Color; + +import java.util.Date; + +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +@Slf4j +public class PeerInfoIconTrading extends PeerInfoIcon { + private final AccountAgeWitnessService accountAgeWitnessService; + private boolean isFiatCurrency; + + public PeerInfoIconTrading(NodeAddress nodeAddress, + String role, + int numTrades, + PrivateNotificationManager privateNotificationManager, + Offer offer, + Preferences preferences, + AccountAgeWitnessService accountAgeWitnessService, + boolean useDevPrivilegeKeys) { + this(nodeAddress, + role, + numTrades, + privateNotificationManager, + offer, + null, + preferences, + accountAgeWitnessService, + useDevPrivilegeKeys); + } + + public PeerInfoIconTrading(NodeAddress nodeAddress, + String role, + int numTrades, + PrivateNotificationManager privateNotificationManager, + Trade trade, + Preferences preferences, + AccountAgeWitnessService accountAgeWitnessService, + boolean useDevPrivilegeKeys) { + this(nodeAddress, + role, + numTrades, + privateNotificationManager, + trade.getOffer(), + trade, + preferences, + accountAgeWitnessService, + useDevPrivilegeKeys); + } + + private PeerInfoIconTrading(NodeAddress nodeAddress, + String role, + int numTrades, + PrivateNotificationManager privateNotificationManager, + @Nullable Offer offer, + @Nullable Trade trade, + Preferences preferences, + AccountAgeWitnessService accountAgeWitnessService, + boolean useDevPrivilegeKeys) { + super(nodeAddress, preferences); + this.numTrades = numTrades; + this.accountAgeWitnessService = accountAgeWitnessService; + if (offer == null) { + checkNotNull(trade, "Trade must not be null if offer is null."); + offer = trade.getOffer(); + } + checkNotNull(offer, "Offer must not be null"); + isFiatCurrency = offer != null && CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()); + initialize(role, offer, trade, privateNotificationManager, useDevPrivilegeKeys); + } + + protected void initialize(String role, Offer offer, Trade trade, PrivateNotificationManager privateNotificationManager, boolean useDevPrivilegeKeys) { + boolean hasTraded = numTrades > 0; + Tuple5 peersAccount = getPeersAccountAge(trade, offer); + + Long accountAge = peersAccount.first; + Long signAge = peersAccount.second; + + String accountAgeTooltip = isFiatCurrency ? + accountAge > -1 ? Res.get("peerInfoIcon.tooltip.age", DisplayUtils.formatAccountAge(accountAge)) : + Res.get("peerInfoIcon.tooltip.unknownAge") : + ""; + tooltipText = hasTraded ? + Res.get("peerInfoIcon.tooltip.trade.traded", role, fullAddress, numTrades, accountAgeTooltip) : + Res.get("peerInfoIcon.tooltip.trade.notTraded", role, fullAddress, accountAgeTooltip); + + createAvatar(getRingColor(offer, trade, accountAge, signAge)); + addMouseListener(numTrades, privateNotificationManager, trade, offer, preferences, useDevPrivilegeKeys, + isFiatCurrency, accountAge, signAge, peersAccount.third, peersAccount.fourth, peersAccount.fifth); + } + + protected Color getRingColor(Offer offer, Trade trade, Long accountAge, Long signAge) { + // outer circle + // for altcoins we always display green + Color ringColor = Color.rgb(0, 225, 0); + if (isFiatCurrency) { + switch (accountAgeWitnessService.getPeersAccountAgeCategory(hasChargebackRisk(trade, offer) ? signAge : accountAge)) { + case TWO_MONTHS_OR_MORE: + ringColor = Color.rgb(0, 225, 0); // > 2 months green + break; + case ONE_TO_TWO_MONTHS: + ringColor = Color.rgb(0, 139, 205); // 1-2 months blue + break; + case LESS_ONE_MONTH: + ringColor = Color.rgb(255, 140, 0); //< 1 month orange + break; + case UNVERIFIED: + default: + ringColor = Color.rgb(255, 0, 0); // not signed, red + break; + } + } + return ringColor; + } + + /** + * @param trade Open trade for trading peer info to be shown + * @param offer Open offer for trading peer info to be shown + * @return account age, sign age, account info, sign info, sign state + */ + private Tuple5 getPeersAccountAge(@Nullable Trade trade, + @Nullable Offer offer) { + AccountAgeWitnessService.SignState signState; + long signAge = -1L; + long accountAge = -1L; + + if (trade != null) { + offer = trade.getOffer(); + if (offer == null) { + // unexpected + return new Tuple5<>(signAge, accountAge, Res.get("peerInfo.age.noRisk"), null, null); + } + signState = accountAgeWitnessService.getSignState(trade); + signAge = accountAgeWitnessService.getWitnessSignAge(trade, new Date()); + accountAge = accountAgeWitnessService.getAccountAge(trade); + } else { + checkNotNull(offer, "Offer must not be null if trade is null."); + signState = accountAgeWitnessService.getSignState(offer); + signAge = accountAgeWitnessService.getWitnessSignAge(offer, new Date()); + accountAge = accountAgeWitnessService.getAccountAge(offer); + } + + if (hasChargebackRisk(trade, offer)) { + String signAgeInfo = Res.get("peerInfo.age.chargeBackRisk"); + String accountSigningState = StringUtils.capitalize(signState.getDisplayString()); + if (signState.equals(AccountAgeWitnessService.SignState.UNSIGNED)) + signAgeInfo = null; + + return new Tuple5<>(accountAge, signAge, Res.get("peerInfo.age.noRisk"), signAgeInfo, accountSigningState); + } + return new Tuple5<>(accountAge, signAge, Res.get("peerInfo.age.noRisk"), null, null); + } + + private static boolean hasChargebackRisk(@Nullable Trade trade, @Nullable Offer offer) { + Offer offerToCheck = trade != null ? trade.getOffer() : offer; + + return offerToCheck != null && + PaymentMethod.hasChargebackRisk(offerToCheck.getPaymentMethod(), offerToCheck.getCurrencyCode()); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 14c78bf5b9..81d877aabf 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -28,7 +28,7 @@ import bisq.desktop.components.AutocompleteComboBox; import bisq.desktop.components.ColoredDecimalPlacesWithZerosText; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.InfoAutoTooltipLabel; -import bisq.desktop.components.PeerInfoIcon; +import bisq.desktop.components.PeerInfoIconTrading; import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.MainView; import bisq.desktop.main.account.AccountView; @@ -1175,7 +1175,7 @@ public class OfferBookView extends ActivatableViewAndModel { +public abstract class DisputeView extends ActivatableView implements PeerInfoIcon.notify { public enum FilterResult { NO_MATCH("No Match"), NO_FILTER("No filter text"), @@ -183,6 +185,7 @@ public abstract class DisputeView extends ActivatableView { private Map chatButtonByDispute = new HashMap<>(); private Map chatBadgeByDispute = new HashMap<>(); private Map newBadgeByDispute = new HashMap<>(); + private Map avatarMap = new HashMap<>(); protected DisputeChatPopup chatPopup; @@ -1182,10 +1185,14 @@ public abstract class DisputeView extends ActivatableView { @Override public void updateItem(final Dispute item, boolean empty) { super.updateItem(item, empty); - if (item != null && !empty) + if (item != null && !empty) { setText(getBuyerOnionAddressColumnLabel(item)); - else + PeerInfoIconDispute peerInfoIconDispute = findOrCreateAvatar(tableRowProperty().get().getIndex(), item.getContract(), true); + setGraphic(peerInfoIconDispute); + } else { setText(""); + setGraphic(null); + } } }; } @@ -1208,10 +1215,14 @@ public abstract class DisputeView extends ActivatableView { @Override public void updateItem(final Dispute item, boolean empty) { super.updateItem(item, empty); - if (item != null && !empty) + if (item != null && !empty) { setText(getSellerOnionAddressColumnLabel(item)); - else + PeerInfoIconDispute peerInfoIconDispute = findOrCreateAvatar(tableRowProperty().get().getIndex(), item.getContract(), false); + setGraphic(peerInfoIconDispute); + } else { setText(""); + setGraphic(null); + } } }; } @@ -1229,7 +1240,7 @@ public abstract class DisputeView extends ActivatableView { long accountAge = accountAgeWitnessService.getAccountAge(contract.getBuyerPaymentAccountPayload(), contract.getBuyerPubKeyRing()); String age = DisplayUtils.formatAccountAge(accountAge); String postFix = CurrencyUtil.isFiatCurrency(item.getContract().getOfferPayload().getCurrencyCode()) ? " / " + age : ""; - return buyerNodeAddress.getHostNameWithoutPostFix() + " (" + nrOfDisputes + postFix + ")"; + return buyerNodeAddress.getHostNameForDisplay() + " (" + nrOfDisputes + postFix + ")"; } else return Res.get("shared.na"); } else { @@ -1246,7 +1257,7 @@ public abstract class DisputeView extends ActivatableView { long accountAge = accountAgeWitnessService.getAccountAge(contract.getSellerPaymentAccountPayload(), contract.getSellerPubKeyRing()); String age = DisplayUtils.formatAccountAge(accountAge); String postFix = CurrencyUtil.isFiatCurrency(item.getContract().getOfferPayload().getCurrencyCode()) ? " / " + age : ""; - return sellerNodeAddress.getHostNameWithoutPostFix() + " (" + nrOfDisputes + postFix + ")"; + return sellerNodeAddress.getHostNameForDisplay() + " (" + nrOfDisputes + postFix + ")"; } else return Res.get("shared.na"); } else { @@ -1432,4 +1443,32 @@ public abstract class DisputeView extends ActivatableView { return (disputeManager instanceof MediationManager) ? Res.get("shared.mediator") : Res.get("shared.refundAgent"); } } + + private PeerInfoIconDispute findOrCreateAvatar(Integer tableRowId, Contract contract, boolean isBuyer) { + NodeAddress nodeAddress = isBuyer ? contract.getBuyerNodeAddress() : contract.getSellerNodeAddress(); + String key = tableRowId + nodeAddress.getHostNameWithoutPostFix() + (isBuyer ? "BUYER" : "SELLER"); + Long accountAge = isBuyer ? + accountAgeWitnessService.getAccountAge(contract.getBuyerPaymentAccountPayload(), contract.getBuyerPubKeyRing()) : + accountAgeWitnessService.getAccountAge(contract.getSellerPaymentAccountPayload(), contract.getSellerPubKeyRing()); + PeerInfoIconDispute peerInfoIcon = new PeerInfoIconDispute( + nodeAddress, + disputeManager.getNrOfDisputes(isBuyer, contract), + accountAge, + preferences); + peerInfoIcon.setCallback(this); + avatarMap.put(key, peerInfoIcon); + log.warn("Creating avatar for {}, map size is now {}", key, avatarMap.size()); + return peerInfoIcon; + } + + @Override + public void avatarTagUpdated() { + // callback from one avatar letting us know that the user updated the tag text. + // we update all avatars, as some could be sharing the same tag + log.info("Updating avatar tags, the avatarMap size is {}", avatarMap.size()); + avatarMap.forEach((key, avatarIcon) -> { + avatarIcon.refreshTag(); + }); + } + } diff --git a/p2p/src/main/java/bisq/network/p2p/NodeAddress.java b/p2p/src/main/java/bisq/network/p2p/NodeAddress.java index 056308cae6..d1aa456118 100644 --- a/p2p/src/main/java/bisq/network/p2p/NodeAddress.java +++ b/p2p/src/main/java/bisq/network/p2p/NodeAddress.java @@ -79,6 +79,17 @@ public final class NodeAddress implements PersistablePayload, NetworkPayload, Us return hostName.replace(".onion", ""); } + // tor v3 onions are too long to display for example in a table grid, so this convenience method + // produces a display-friendly format which includes [first 7]..[last 7] characters. + // tor v2 and localhost will be displayed in full, as they are 16 chars or less. + public String getHostNameForDisplay() { + String work = getHostNameWithoutPostFix(); + if (work.length() > 16) { + return work.substring(0, 7) + ".." + work.substring(work.length() - 7); + } + return work; + } + // We use just a few chars from the full address to blur the potential receiver for sent network_messages public byte[] getAddressPrefixHash() { if (addressPrefixHash == null) From 2a7e2cc22cda48b2ae65731cb76926e13e9af5c1 Mon Sep 17 00:00:00 2001 From: jmacxx <47253594+jmacxx@users.noreply.github.com> Date: Wed, 9 Jun 2021 23:51:13 -0500 Subject: [PATCH 2/2] code review changes. Co-authored-by: @KaiWitt --- .../bisq/desktop/components/PeerInfoIcon.java | 6 +++++ .../components/PeerInfoIconDispute.java | 15 +++-------- .../components/PeerInfoIconTrading.java | 25 +++++++++---------- .../main/support/dispute/DisputeView.java | 1 - .../main/java/bisq/desktop/util/Colors.java | 6 +++++ 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java index 9566cdce44..d3ae4b4fba 100644 --- a/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIcon.java @@ -193,6 +193,12 @@ public class PeerInfoIcon extends Group { return 1; } + protected String getAccountAgeTooltip(Long accountAge) { + return accountAge > -1 ? + Res.get("peerInfoIcon.tooltip.age", DisplayUtils.formatAccountAge(accountAge)) : + Res.get("peerInfoIcon.tooltip.unknownAge"); + } + protected void updatePeerInfoIcon() { String tag; Map peerTagMap = preferences.getPeerTagMap(); diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java index adeeead467..178d9750ed 100644 --- a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconDispute.java @@ -17,17 +17,15 @@ package bisq.desktop.components; -import bisq.desktop.util.DisplayUtils; - import bisq.core.locale.Res; import bisq.core.user.Preferences; import bisq.network.p2p.NodeAddress; -import javafx.scene.paint.Color; - import lombok.extern.slf4j.Slf4j; +import static bisq.desktop.util.Colors.AVATAR_GREY; + @Slf4j public class PeerInfoIconDispute extends PeerInfoIcon { @@ -37,15 +35,10 @@ public class PeerInfoIconDispute extends PeerInfoIcon { Preferences preferences) { super(nodeAddress, preferences); - String accountAgeTooltip = accountAge > -1 ? - Res.get("peerInfoIcon.tooltip.age", DisplayUtils.formatAccountAge(accountAge)) : - Res.get("peerInfoIcon.tooltip.unknownAge"); - - tooltipText = Res.get("peerInfoIcon.tooltip.dispute", fullAddress, nrOfDisputes, accountAgeTooltip); + tooltipText = Res.get("peerInfoIcon.tooltip.dispute", fullAddress, nrOfDisputes, getAccountAgeTooltip(accountAge)); // outer circle always display gray - Color ringColor = Color.rgb(128, 128, 128); - createAvatar(ringColor); + createAvatar(AVATAR_GREY); addMouseListener(numTrades, null, null, null, preferences, false, false, accountAge, 0L, null, null, null); } diff --git a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java index 20a27b4c3f..fa063a2bf6 100644 --- a/desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java +++ b/desktop/src/main/java/bisq/desktop/components/PeerInfoIconTrading.java @@ -17,8 +17,6 @@ package bisq.desktop.components; -import bisq.desktop.util.DisplayUtils; - import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.alert.PrivateNotificationManager; import bisq.core.locale.CurrencyUtil; @@ -42,6 +40,7 @@ import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; +import static bisq.desktop.util.Colors.*; import static com.google.common.base.Preconditions.checkNotNull; @Slf4j @@ -115,37 +114,37 @@ public class PeerInfoIconTrading extends PeerInfoIcon { Long accountAge = peersAccount.first; Long signAge = peersAccount.second; - String accountAgeTooltip = isFiatCurrency ? - accountAge > -1 ? Res.get("peerInfoIcon.tooltip.age", DisplayUtils.formatAccountAge(accountAge)) : - Res.get("peerInfoIcon.tooltip.unknownAge") : - ""; tooltipText = hasTraded ? - Res.get("peerInfoIcon.tooltip.trade.traded", role, fullAddress, numTrades, accountAgeTooltip) : - Res.get("peerInfoIcon.tooltip.trade.notTraded", role, fullAddress, accountAgeTooltip); + Res.get("peerInfoIcon.tooltip.trade.traded", role, fullAddress, numTrades, getAccountAgeTooltip(accountAge)) : + Res.get("peerInfoIcon.tooltip.trade.notTraded", role, fullAddress, getAccountAgeTooltip(accountAge)); createAvatar(getRingColor(offer, trade, accountAge, signAge)); addMouseListener(numTrades, privateNotificationManager, trade, offer, preferences, useDevPrivilegeKeys, isFiatCurrency, accountAge, signAge, peersAccount.third, peersAccount.fourth, peersAccount.fifth); } + protected String getAccountAgeTooltip(Long accountAge) { + return isFiatCurrency ? super.getAccountAgeTooltip(accountAge) : ""; + } + protected Color getRingColor(Offer offer, Trade trade, Long accountAge, Long signAge) { // outer circle // for altcoins we always display green - Color ringColor = Color.rgb(0, 225, 0); + Color ringColor = AVATAR_GREEN; if (isFiatCurrency) { switch (accountAgeWitnessService.getPeersAccountAgeCategory(hasChargebackRisk(trade, offer) ? signAge : accountAge)) { case TWO_MONTHS_OR_MORE: - ringColor = Color.rgb(0, 225, 0); // > 2 months green + ringColor = AVATAR_GREEN; break; case ONE_TO_TWO_MONTHS: - ringColor = Color.rgb(0, 139, 205); // 1-2 months blue + ringColor = AVATAR_BLUE; break; case LESS_ONE_MONTH: - ringColor = Color.rgb(255, 140, 0); //< 1 month orange + ringColor = AVATAR_ORANGE; break; case UNVERIFIED: default: - ringColor = Color.rgb(255, 0, 0); // not signed, red + ringColor = AVATAR_RED; break; } } diff --git a/desktop/src/main/java/bisq/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/bisq/desktop/main/support/dispute/DisputeView.java index 6c4e3a9d94..f193d5053b 100644 --- a/desktop/src/main/java/bisq/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/bisq/desktop/main/support/dispute/DisputeView.java @@ -1457,7 +1457,6 @@ public abstract class DisputeView extends ActivatableView implements preferences); peerInfoIcon.setCallback(this); avatarMap.put(key, peerInfoIcon); - log.warn("Creating avatar for {}, map size is now {}", key, avatarMap.size()); return peerInfoIcon; } diff --git a/desktop/src/main/java/bisq/desktop/util/Colors.java b/desktop/src/main/java/bisq/desktop/util/Colors.java index d102e9eedb..fb7f76302e 100644 --- a/desktop/src/main/java/bisq/desktop/util/Colors.java +++ b/desktop/src/main/java/bisq/desktop/util/Colors.java @@ -24,4 +24,10 @@ public class Colors { public static final Paint BLUE = Color.valueOf("#0f87c3"); public static final Paint LIGHT_GREY = Color.valueOf("#CCCCCC"); public static final Paint GREEN = Color.valueOf("#00aa33"); + + public static final Color AVATAR_RED = Color.rgb(255, 0, 0); + public static final Color AVATAR_ORANGE = Color.rgb(255, 140, 0); + public static final Color AVATAR_BLUE = Color.rgb(0, 139, 205); + public static final Color AVATAR_GREEN = Color.rgb(0, 225, 0); + public static final Color AVATAR_GREY = Color.rgb(128, 128, 128); }