Merge pull request #5559 from jmacxx/dispute_tag_avatar

Feature to enable disputes user avatars and tag editing
This commit is contained in:
Christoph Atteneder 2021-06-15 09:45:26 +02:00 committed by GitHub
commit ddd2ab490b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 345 additions and 182 deletions

View file

@ -3071,6 +3071,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

View file

@ -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<String, String> 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<Long, Long, String, String, String> 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<Long, Long, String, String, String> 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());
}
@ -340,8 +193,15 @@ 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<String, String> peerTagMap = preferences.getPeerTagMap();
if (peerTagMap.containsKey(fullAddress)) {
tag = peerTagMap.get(fullAddress);
final String text = !tag.isEmpty() ? Res.get("peerInfoIcon.tooltip", tooltipText, tag) : tooltipText;

View file

@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.components;
import bisq.core.locale.Res;
import bisq.core.user.Preferences;
import bisq.network.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
import static bisq.desktop.util.Colors.AVATAR_GREY;
@Slf4j
public class PeerInfoIconDispute extends PeerInfoIcon {
public PeerInfoIconDispute(NodeAddress nodeAddress,
String nrOfDisputes,
long accountAge,
Preferences preferences) {
super(nodeAddress, preferences);
tooltipText = Res.get("peerInfoIcon.tooltip.dispute", fullAddress, nrOfDisputes, getAccountAgeTooltip(accountAge));
// outer circle always display gray
createAvatar(AVATAR_GREY);
addMouseListener(numTrades, null, null, null, preferences, false,
false, accountAge, 0L, null, null, null);
}
public void refreshTag() {
updatePeerInfoIcon();
}
}

View file

@ -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,

View file

@ -0,0 +1,198 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.components;
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 bisq.desktop.util.Colors.*;
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<Long, Long, String, String, String> peersAccount = getPeersAccountAge(trade, offer);
Long accountAge = peersAccount.first;
Long signAge = peersAccount.second;
tooltipText = hasTraded ?
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 = AVATAR_GREEN;
if (isFiatCurrency) {
switch (accountAgeWitnessService.getPeersAccountAgeCategory(hasChargebackRisk(trade, offer) ? signAge : accountAge)) {
case TWO_MONTHS_OR_MORE:
ringColor = AVATAR_GREEN;
break;
case ONE_TO_TWO_MONTHS:
ringColor = AVATAR_BLUE;
break;
case LESS_ONE_MONTH:
ringColor = AVATAR_ORANGE;
break;
case UNVERIFIED:
default:
ringColor = AVATAR_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<Long, Long, String, String, String> 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());
}
}

View file

@ -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<GridPane, OfferBookVi
final NodeAddress makersNodeAddress = offer.getOwnerNodeAddress();
String role = Res.get("peerInfoIcon.tooltip.maker");
int numTrades = model.getNumTrades(offer);
PeerInfoIcon peerInfoIcon = new PeerInfoIcon(makersNodeAddress,
PeerInfoIconTrading peerInfoIcon = new PeerInfoIconTrading(makersNodeAddress,
role,
numTrades,
privateNotificationManager,

View file

@ -25,7 +25,7 @@ import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.PeerInfoIcon;
import bisq.desktop.components.PeerInfoIconTrading;
import bisq.desktop.main.MainView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.ClosedTradesSummaryWindow;
@ -575,7 +575,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
int numPastTrades = model.getNumPastTrades(trade);
final NodeAddress tradingPeerNodeAddress = trade.getTradingPeerNodeAddress();
String role = Res.get("peerInfoIcon.tooltip.tradePeer");
Node peerInfoIcon = new PeerInfoIcon(tradingPeerNodeAddress,
Node peerInfoIcon = new PeerInfoIconTrading(tradingPeerNodeAddress,
role,
numPastTrades,
privateNotificationManager,

View file

@ -22,7 +22,7 @@ import bisq.desktop.common.view.ActivatableViewAndModel;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.PeerInfoIcon;
import bisq.desktop.components.PeerInfoIconTrading;
import bisq.desktop.main.MainView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
@ -828,7 +828,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
final NodeAddress tradingPeerNodeAddress = trade.getTradingPeerNodeAddress();
int numPastTrades = model.getNumPastTrades(trade);
String role = Res.get("peerInfoIcon.tooltip.tradePeer");
Node peerInfoIcon = new PeerInfoIcon(tradingPeerNodeAddress,
Node peerInfoIcon = new PeerInfoIconTrading(tradingPeerNodeAddress,
role,
numPastTrades,
privateNotificationManager,

View file

@ -23,6 +23,8 @@ import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.PeerInfoIcon;
import bisq.desktop.components.PeerInfoIconDispute;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.ContractWindow;
import bisq.desktop.main.overlays.windows.DisputeSummaryWindow;
@ -119,7 +121,7 @@ import javax.annotation.Nullable;
import static bisq.desktop.util.FormBuilder.getIconForLabel;
import static bisq.desktop.util.FormBuilder.getRegularIconButton;
public abstract class DisputeView extends ActivatableView<VBox, Void> {
public abstract class DisputeView extends ActivatableView<VBox, Void> 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<VBox, Void> {
private Map<String, Button> chatButtonByDispute = new HashMap<>();
private Map<String, JFXBadge> chatBadgeByDispute = new HashMap<>();
private Map<String, JFXBadge> newBadgeByDispute = new HashMap<>();
private Map<String, PeerInfoIconDispute> avatarMap = new HashMap<>();
protected DisputeChatPopup chatPopup;
@ -1183,10 +1186,14 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
@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);
}
}
};
}
@ -1209,10 +1216,14 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
@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);
}
}
};
}
@ -1230,7 +1241,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
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 {
@ -1247,7 +1258,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
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 {
@ -1433,4 +1444,31 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
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);
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();
});
}
}

View file

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

View file

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