diff --git a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java index 0f6ba95970..73f22a5ebf 100644 --- a/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java +++ b/core/src/main/java/bisq/core/trade/txproof/AssetTxProofResult.java @@ -45,6 +45,10 @@ public enum AssetTxProofResult { @Getter private int numRequiredSuccessResults; @Getter + private int numConfirmations; + @Getter + private int numRequiredConfirmations; + @Getter private String details = ""; // If isTerminal is set it means that we stop the service @Getter @@ -69,6 +73,16 @@ public enum AssetTxProofResult { return this; } + public AssetTxProofResult numConfirmations(int numConfirmations) { + this.numConfirmations = numConfirmations; + return this; + } + + public AssetTxProofResult numRequiredConfirmations(int numRequiredConfirmations) { + this.numRequiredConfirmations = numRequiredConfirmations; + return this; + } + public AssetTxProofResult details(String details) { this.details = details; return this; @@ -79,6 +93,8 @@ public enum AssetTxProofResult { return "AssetTxProofResult{" + "\n numSuccessResults=" + numSuccessResults + ",\n requiredSuccessResults=" + numRequiredSuccessResults + + ",\n numConfirmations=" + numConfirmations + + ",\n numRequiredConfirmations=" + numRequiredConfirmations + ",\n details='" + details + '\'' + "\n} " + super.toString(); } diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java index cf7b7835cf..5a11cbc5c6 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofParser.java @@ -162,7 +162,7 @@ public class XmrTxProofParser implements AssetTxProofParser TX_NOT_FOUND, // Tx not visible in network yet. Could be also other error PENDING_CONFIRMATIONS, + SUCCESS, + // Error states CONNECTION_FAILURE, API_INVALID, diff --git a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java index 774423b329..634f6b8ae7 100644 --- a/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java +++ b/core/src/main/java/bisq/core/trade/txproof/xmr/XmrTxProofRequestsPerTrade.java @@ -171,7 +171,12 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade { // have completed on the service level. log.info("All {} tx proof requests for trade {} have been successful.", numRequiredSuccessResults, trade.getShortId()); - assetTxProofResult = AssetTxProofResult.COMPLETED; + XmrTxProofRequest.Detail detail = result.getDetail(); + assetTxProofResult = AssetTxProofResult.COMPLETED + .numSuccessResults(numSuccessResults) + .numRequiredSuccessResults(numRequiredSuccessResults) + .numConfirmations(detail != null ? detail.getNumConfirmations() : 0) + .numRequiredConfirmations(autoConfirmSettings.getRequiredConfirmations()); } break; case FAILED: @@ -292,6 +297,8 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade { return AssetTxProofResult.PENDING .numSuccessResults(numSuccessResults) .numRequiredSuccessResults(numRequiredSuccessResults) + .numConfirmations(detail != null ? detail.getNumConfirmations() : 0) + .numRequiredConfirmations(autoConfirmSettings.getRequiredConfirmations()) .details(detailString); } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 177e0cad3d..c5181e56b8 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -572,9 +572,9 @@ portfolio.pending.step5.completed=Completed portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status portfolio.pending.autoConf=Auto-confirmed - +portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1} portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute. -portfolio.pending.autoConf.state.confirmations=Confirmations: {0}/{1} +portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1} portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers. diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java index 4bff3492df..5f25baaa09 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java @@ -18,8 +18,10 @@ package bisq.desktop.main.portfolio.pendingtrades.steps.seller; import bisq.desktop.components.BusyAnimation; +import bisq.desktop.components.InfoTextField; import bisq.desktop.components.TextFieldWithCopyIcon; import bisq.desktop.components.TitledGroupBg; +import bisq.desktop.components.indicator.TxConfidenceIndicator; import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel; import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView; @@ -49,6 +51,7 @@ import bisq.core.user.DontShowAgainLookup; import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.app.DevEnv; +import bisq.common.util.Tuple2; import bisq.common.util.Tuple4; import javafx.scene.control.Button; @@ -57,6 +60,9 @@ import javafx.scene.control.Tooltip; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; + +import javafx.geometry.Insets; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -65,20 +71,18 @@ import javafx.beans.value.ChangeListener; import java.util.Optional; -import static bisq.desktop.util.FormBuilder.addButtonBusyAnimationLabelAfterGroup; -import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; -import static bisq.desktop.util.FormBuilder.addTopLabelTextFieldWithCopyIcon; +import static bisq.desktop.util.FormBuilder.*; public class SellerStep3View extends TradeStepView { + private final ChangeListener proofResultListener; private Button confirmButton; private Label statusLabel; private BusyAnimation busyAnimation; private Subscription tradeStatePropertySubscription; private Timer timeoutTimer; - private TextFieldWithCopyIcon assetTxProofResultField; - private final ChangeListener proofResultListener; + private InfoTextField assetTxProofResultField; + private TxConfidenceIndicator assetTxConfidenceIndicator; /////////////////////////////////////////////////////////////////////////////////////////// @@ -230,9 +234,28 @@ public class SellerStep3View extends TradeStepView { } if (isBlockChain && trade.getOffer().getCurrencyCode().equals("XMR")) { - assetTxProofResultField = addTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, - Res.get("portfolio.pending.step3_seller.autoConf.status.label"), - "", Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE).second; + assetTxProofResultField = new InfoTextField(); + + Tuple2 topLabelWithVBox = getTopLabelWithVBox(Res.get("portfolio.pending.step3_seller.autoConf.status.label"), assetTxProofResultField); + VBox vBox = topLabelWithVBox.second; + + assetTxConfidenceIndicator = new TxConfidenceIndicator(); + assetTxConfidenceIndicator.setId("xmr-confidence"); + assetTxConfidenceIndicator.setProgress(0); + assetTxConfidenceIndicator.setTooltip(new Tooltip()); + assetTxProofResultField.setContentForInfoPopOver(createPopoverLabel(Res.get("setting.info.msg"))); + + HBox.setMargin(assetTxConfidenceIndicator, new Insets(Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0)); + + HBox hBox = new HBox(); + HBox.setHgrow(vBox, Priority.ALWAYS); + hBox.setSpacing(10); + hBox.getChildren().addAll(vBox, assetTxConfidenceIndicator); + + GridPane.setRowIndex(hBox, gridRow); + GridPane.setColumnIndex(hBox, 1); + GridPane.setMargin(hBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE + Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0)); + gridPane.getChildren().add(hBox); } TextFieldWithCopyIcon myPaymentDetailsTextField = addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, @@ -274,7 +297,6 @@ public class SellerStep3View extends TradeStepView { // Info /////////////////////////////////////////////////////////////////////////////////////////// - @Override protected String getInfoText() { String currencyCode = model.dataModel.getCurrencyCode(); @@ -446,7 +468,36 @@ public class SellerStep3View extends TradeStepView { private void applyAssetTxProofResult(AssetTxProofResult result) { String txt = GUIUtil.getProofResultAsString(result); assetTxProofResultField.setText(txt); - assetTxProofResultField.setTooltip(new Tooltip(txt)); + switch (result) { + case PENDING: + case COMPLETED: + if (result.getNumRequiredConfirmations() > 0) { + int numRequiredConfirmations = result.getNumRequiredConfirmations(); + int numConfirmations = result.getNumConfirmations(); + if (numConfirmations == 0) { + assetTxConfidenceIndicator.setProgress(-1); + } else { + double progress = Math.min(1, (double) numConfirmations / (double) numRequiredConfirmations); + assetTxConfidenceIndicator.setProgress(progress); + assetTxConfidenceIndicator.getTooltip().setText( + Res.get("portfolio.pending.autoConf.blocks", + numConfirmations, numRequiredConfirmations)); + } + } + break; + default: + // Set invisible by default + assetTxConfidenceIndicator.setProgress(0); + break; + } + } + + private Label createPopoverLabel(String text) { + Label label = new Label(text); + label.setPrefWidth(600); + label.setWrapText(true); + label.setPadding(new Insets(10)); + return label; } @Override