Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use account signing state in accounts view #3365

Merged
merged 7 commits into from Oct 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -19,6 +19,7 @@

import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.locale.TradeCurrency;
import bisq.core.payment.TradeLimits;

import bisq.common.proto.persistable.PersistablePayload;
Expand Down Expand Up @@ -322,6 +323,11 @@ public boolean isAsset() {
return this.equals(BLOCK_CHAINS_INSTANT) || this.equals(BLOCK_CHAINS);
}

public static boolean hasChargebackRisk(PaymentMethod paymentMethod, List<TradeCurrency> tradeCurrencies) {
return tradeCurrencies.stream()
.anyMatch(tradeCurrency -> hasChargebackRisk(paymentMethod, tradeCurrency.getCode()));
}

public static boolean hasChargebackRisk(PaymentMethod paymentMethod, String currencyCode) {
if (paymentMethod == null)
return false;
Expand Down
9 changes: 6 additions & 3 deletions core/src/main/resources/i18n/displayStrings.properties
Expand Up @@ -339,11 +339,13 @@ offerbook.timeSinceSigning.info.peer=signed by a peer, waiting for limits to be
offerbook.timeSinceSigning.info.peerLimitLifted=signed by a peer and limits were lifted
offerbook.timeSinceSigning.info.signer=signed by peer and can sign peers accounts
offerbook.timeSinceSigning.daysSinceSigning={0} days
offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing

offerbook.timeSinceSigning.help=By trading with a payment account that was verified by an arbitrator or a peer, your account gets signed as well.\n\
30 days later the initial limit of 0.01 BTC gets lifted and after 90 days your account can sign other peers as well.
offerbook.timeSinceSigning.notSigned=Not signed yet
offerbook.timeSinceSigning.notSigned.noNeed=Unsigned
shared.notSigned=This account hasn't been signed yet.
shared.notSigned=This account hasn't been signed yet
shared.notSigned.noNeed=This account type doesn't use signing

offerbook.nrOffers=No. of offers: {0}
Expand Down Expand Up @@ -726,8 +728,9 @@ portfolio.pending.step3_seller.onPaymentReceived.name=Please also verify that th
portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon you have confirmed the receipt, the locked trade amount will be released to the BTC buyer and the security deposit will be refunded.\n\n
portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirm that you have received the payment
portfolio.pending.step3_seller.onPaymentReceived.confirm.yes=Yes, I have received the payment
portfolio.pending.step3_seller.onPaymentReceived.signer=By confirming receipt of payment you verify that the \
counterparty has acted according to the trade protocol.
portfolio.pending.step3_seller.onPaymentReceived.signer=IMPORTANT: By confirming the receipt of payment you'll also \
verify the account of the counterparty and sign it accordingly. As the account of the counterparty hasn't been signed yet \
it is advised to delay the confirmation of the received payment as long as possible to reduce the risk of a chargeback.

portfolio.pending.step5_buyer.groupTitle=Summary of completed trade
portfolio.pending.step5_buyer.tradeFee=Trade fee
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/main/java/bisq/desktop/bisq.css
Expand Up @@ -723,7 +723,7 @@ tree-table-view:focused {
* Icons *
* *
******************************************************************************/
.icon, icon:hover {
.icon, .icon:hover {
-fx-cursor: hand;
}

Expand Down
Expand Up @@ -17,9 +17,10 @@

package bisq.desktop.components;

import bisq.common.UserThread;
import bisq.desktop.components.controlsfx.control.PopOver;

import bisq.common.UserThread;

import de.jensd.fx.fontawesome.AwesomeIcon;
import de.jensd.fx.glyphs.GlyphIcons;

Expand All @@ -42,8 +43,7 @@ public class InfoAutoTooltipLabel extends AutoTooltipLabel {
private ContentDisplay contentDisplay;

public InfoAutoTooltipLabel(String text, GlyphIcons icon, ContentDisplay contentDisplay, String info) {
super(text);
this.contentDisplay = contentDisplay;
this(text, contentDisplay);

setIcon(icon);
positionAndActivateIcon(contentDisplay, info, DEFAULT_WIDTH);
Expand All @@ -56,6 +56,11 @@ public InfoAutoTooltipLabel(String text, AwesomeIcon icon, ContentDisplay conten
positionAndActivateIcon(contentDisplay, info, width);
}

public InfoAutoTooltipLabel(String text, ContentDisplay contentDisplay) {
super(text);
this.contentDisplay = contentDisplay;
}

public void setIcon(GlyphIcons icon) {
textIcon = getIcon(icon);
}
Expand All @@ -69,6 +74,11 @@ public void setIcon(AwesomeIcon icon) {
textIcon = getIcon(icon);
}

public void hideIcon() {
textIcon = null;
setGraphic(textIcon);
}

private void positionAndActivateIcon(ContentDisplay contentDisplay, String info, double width) {
textIcon.setOpacity(0.4);

Expand Down
37 changes: 16 additions & 21 deletions desktop/src/main/java/bisq/desktop/components/InfoTextField.java
Expand Up @@ -17,16 +17,19 @@

package bisq.desktop.components;

import bisq.common.UserThread;
import bisq.desktop.components.controlsfx.control.PopOver;

import bisq.common.UserThread;

import de.jensd.fx.fontawesome.AwesomeIcon;
import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;

import com.jfoenix.controls.JFXTextField;

import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Text;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
Expand All @@ -39,6 +42,7 @@
import lombok.Getter;

import static bisq.desktop.util.FormBuilder.getIcon;
import static bisq.desktop.util.FormBuilder.getRegularIconForLabel;

public class InfoTextField extends AnchorPane {
public static final Logger log = LoggerFactory.getLogger(InfoTextField.class);
Expand All @@ -48,7 +52,6 @@ public class InfoTextField extends AnchorPane {

private final StringProperty text = new SimpleStringProperty();
protected final Label infoIcon;
protected final Label privacyIcon;
private Label currentIcon;
private Boolean hidePopover;
private PopOver popover;
Expand All @@ -68,18 +71,13 @@ public InfoTextField() {
infoIcon.setLayoutY(5);
infoIcon.getStyleClass().addAll("icon", "info");

privacyIcon = getIcon(AwesomeIcon.EYE_CLOSE);
privacyIcon.setLayoutY(5);
privacyIcon.getStyleClass().addAll("icon", "info");

AnchorPane.setRightAnchor(infoIcon, 7.0);
AnchorPane.setRightAnchor(privacyIcon, 7.0);
AnchorPane.setRightAnchor(textField, 0.0);
AnchorPane.setLeftAnchor(textField, 0.0);

hideIcons();

getChildren().addAll(textField, infoIcon, privacyIcon);
getChildren().addAll(textField, infoIcon);
}


Expand All @@ -96,21 +94,20 @@ public void setContentForInfoPopOver(Node node) {
setActionHandlers(node);
}

public void setContentForPrivacyPopOver(Node node) {
currentIcon = privacyIcon;

public void setContent(MaterialDesignIcon icon, String info, String style, double opacity) {
hideIcons();
setActionHandlers(node);
}

public void setIconsLeftAligned() {
arrowLocation = PopOver.ArrowLocation.LEFT_TOP;;
currentIcon = new Label();
Text textIcon = getRegularIconForLabel(icon, currentIcon);

setActionHandlers(new Label(info));

AnchorPane.clearConstraints(infoIcon);
AnchorPane.clearConstraints(privacyIcon);
currentIcon.setLayoutY(5);
textIcon.getStyleClass().addAll("icon", style);
currentIcon.setOpacity(opacity);
AnchorPane.setRightAnchor(currentIcon, 7.0);

AnchorPane.setLeftAnchor(infoIcon, 7.0);
AnchorPane.setLeftAnchor(privacyIcon, 7.0);
getChildren().add(currentIcon);
}

///////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -119,8 +116,6 @@ public void setIconsLeftAligned() {
private void hideIcons() {
infoIcon.setManaged(false);
infoIcon.setVisible(false);
privacyIcon.setManaged(false);
privacyIcon.setVisible(false);
}

private void setActionHandlers(Node node) {
Expand Down
Expand Up @@ -25,6 +25,7 @@
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout;

import bisq.core.account.witness.AccountAgeWitness;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.locale.Country;
import bisq.core.locale.CurrencyUtil;
Expand All @@ -35,6 +36,7 @@
import bisq.core.offer.OfferPayload;
import bisq.core.payment.AssetAccount;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.util.BSFormatter;
import bisq.core.util.validation.InputValidator;

Expand All @@ -45,6 +47,8 @@

import org.apache.commons.lang3.StringUtils;

import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;

import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
Expand All @@ -61,7 +65,9 @@

import javafx.util.StringConverter;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

Expand Down Expand Up @@ -194,26 +200,46 @@ else if (!paymentAccount.getTradeCurrencies().isEmpty())
addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), limitationsText);

String accountSigningStateText;

if (accountAgeWitnessService.myHasSignedWitness(paymentAccount.getPaymentAccountPayload())) {
//TODO sqrrm: We need four states in here:
// - signed by arbitrator
// - signed by peer
// - signed by peer and limit lifted
// - signed by peer and able to sign
// Additionally we need to have some enum or so how the account signing took place.
// e.g. if in the future we'll also offer the "pay with two different accounts"-signing
accountSigningStateText = "This account was verified and signed by an arbitrator or peer / Time since signing: 3 days";
} else {
//TODO sqrrm: Here we need two states:
// - not signing necessary for this payment account
// - signing required and not signed
accountSigningStateText = Res.get("shared.notSigned");
MaterialDesignIcon icon;

boolean needsSigning = PaymentMethod.hasChargebackRisk(paymentAccount.getPaymentMethod(),
paymentAccount.getTradeCurrencies());

if (needsSigning) {

AccountAgeWitness myWitness = accountAgeWitnessService.getMyWitness(
paymentAccount.paymentAccountPayload);
AccountAgeWitnessService.SignState signState =
accountAgeWitnessService.getSignState(myWitness);

accountSigningStateText = StringUtils.capitalize(signState.getPresentation());

long daysSinceSigning = TimeUnit.MILLISECONDS.toDays(
accountAgeWitnessService.getWitnessSignAge(myWitness, new Date()));
String timeSinceSigning = Res.get("offerbook.timeSinceSigning.daysSinceSigning.long",
Res.get("offerbook.timeSinceSigning.daysSinceSigning",
daysSinceSigning));

switch (signState) {
case PEER_SIGNER:
case ARBITRATOR:
icon = MaterialDesignIcon.APPROVAL;
accountSigningStateText += " / " + timeSinceSigning;
break;
case PEER_INITIAL:
case PEER_LIMIT_LIFTED:
accountSigningStateText += " / " + timeSinceSigning;
default:
icon = MaterialDesignIcon.ALERT_CIRCLE_OUTLINE;
}

InfoTextField accountSigningField = addCompactTopLabelInfoTextField(gridPane, ++gridRow, Res.get("shared.accountSigningState"),
accountSigningStateText).second;
//TODO: add additional information regarding account signing
accountSigningField.setContent(icon, accountSigningStateText, "", 0.4);
}

addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.accountSigningState"), accountSigningStateText);
}
else
} else
addTopLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), limitationsText);

if (!(paymentAccount instanceof AssetAccount)) {
Expand Down
Expand Up @@ -88,7 +88,8 @@ public void addFormForAddAccount() {

private void addCurrenciesGrid(boolean isEditable) {
FlowPane flowPane = addTopLabelFlowPane(gridPane, ++gridRow,
Res.get("payment.supportedCurrencies"), 0).second;
Res.get("payment.supportedCurrencies"), Layout.FLOATING_LABEL_DISTANCE,
Layout.FLOATING_LABEL_DISTANCE).second;

if (isEditable)
flowPane.setId("flow-pane-checkboxes-bg");
Expand Down
Expand Up @@ -12,9 +12,12 @@
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.locale.Res;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;

import bisq.common.UserThread;

import org.apache.commons.lang3.StringUtils;

import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;

import javafx.scene.Node;
Expand Down Expand Up @@ -97,7 +100,7 @@ protected void setPaymentAccountsCellFactory() {
@Override
public ListCell<PaymentAccount> call(ListView<PaymentAccount> list) {
return new ListCell<>() {
final InfoAutoTooltipLabel label = new InfoAutoTooltipLabel("", MaterialDesignIcon.ALERT_CIRCLE_OUTLINE, ContentDisplay.RIGHT, "");
final InfoAutoTooltipLabel label = new InfoAutoTooltipLabel("", ContentDisplay.RIGHT);
final ImageView icon = ImageUtil.getImageViewById(ImageUtil.REMOVE_ICON);
final Button removeButton = new AutoTooltipButton("", icon);
final AnchorPane pane = new AnchorPane(label, removeButton);
Expand All @@ -114,21 +117,28 @@ public void updateItem(final PaymentAccount item, boolean empty) {
if (item != null && !empty) {
label.setText(item.getAccountName());

if (accountAgeWitnessService.myHasSignedWitness(item.paymentAccountPayload)) {
//TODO sqrrm: We need four states in here:
// - signed by arbitrator
// - signed by peer
// - signed by peer and limit lifted
// - signed by peer and able to sign
// Additionally we need to have some enum or so how the account signing took place.
// e.g. if in the future we'll also offer the "pay with two different accounts"-signing
label.setIcon(MaterialDesignIcon.APPROVAL, "This account was verified and signed by an arbitrator or peer.");
boolean needsSigning = PaymentMethod.hasChargebackRisk(item.getPaymentMethod(),
item.getTradeCurrencies());

if (needsSigning) {
AccountAgeWitnessService.SignState signState =
accountAgeWitnessService.getSignState(accountAgeWitnessService.getMyWitness(
item.paymentAccountPayload));

String info = StringUtils.capitalize(signState.getPresentation());

switch (signState) {
case PEER_SIGNER:
case ARBITRATOR:
label.setIcon(MaterialDesignIcon.APPROVAL, info);
break;
default:
label.setIcon(MaterialDesignIcon.ALERT_CIRCLE_OUTLINE, info);
}
} else {
//TODO sqrrm: Here we need two states:
// - not signing necessary for this payment account
// - signing required and not signed
label.setIcon(MaterialDesignIcon.ALERT_CIRCLE_OUTLINE, Res.get("shared.notSigned"));
label.hideIcon();
}

removeButton.setOnAction(e -> onDeleteAccount(item));
setGraphic(pane);
} else {
Expand Down
Expand Up @@ -494,7 +494,7 @@ private void showPopup() {
CurrencyUtil.getNameByCode(trade.getOffer().getCurrencyCode()),
amount) +
accountDetails +
paymentDetailsForTradePopup + ".\n\n" +
paymentDetailsForTradePopup + "\n\n" +
copyPaste;
} else if (paymentAccountPayload instanceof CashDepositAccountPayload) {
message += Res.get("portfolio.pending.step2_buyer.cash",
Expand Down
Expand Up @@ -350,6 +350,10 @@ else if (paymentAccountPayload instanceof F2FAccountPayload)
if (optionalHolderName.isPresent()) {
message = message + Res.get("portfolio.pending.step3_seller.bankCheck", optionalHolderName.get(), part);
}

if (model.isSignWitnessTrade(true)) {
message += Res.get("portfolio.pending.step3_seller.onPaymentReceived.signer");
}
}
if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key)) {
DontShowAgainLookup.dontShowAgain(key, true);
Expand Down