From 57dba5d6aefa92eda1c73e4804b2f4e86b191929 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Fri, 12 Jan 2024 12:10:38 +0200 Subject: [PATCH] improve input and output labels on transaction tree and detail panels --- drongo | 2 +- .../sparrow/control/TransactionDiagram.java | 205 +----------------- .../control/TransactionDiagramLabel.java | 24 +- .../sparrow/glyphfont/FontAwesome5.java | 2 + .../sparrow/glyphfont/GlyphUtils.java | 178 +++++++++++++++ .../transaction/HeadersController.java | 11 +- .../sparrow/transaction/InputController.java | 19 +- .../sparrow/transaction/InputForm.java | 28 ++- .../sparrow/transaction/OutputController.java | 38 +++- .../sparrow/transaction/OutputForm.java | 43 +++- .../transaction/TransactionController.java | 29 +-- .../sparrow/transaction/TransactionData.java | 22 +- .../sparrow/transaction/TransactionForm.java | 26 ++- .../sparrow/transaction/transaction.css | 6 +- 14 files changed, 367 insertions(+), 266 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java diff --git a/drongo b/drongo index 78944a71..42f279e5 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 78944a7114f5e22d0622dd07ca426e04acdce5b7 +Subproject commit 42f279e5e7cfdcf0de80f60f65857d26db8580ad diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java index 8234adbc..e2641b1c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java @@ -14,6 +14,7 @@ import com.sparrowwallet.sparrow.event.ExcludeUtxoEvent; import com.sparrowwallet.sparrow.event.ReplaceChangeAddressEvent; import com.sparrowwallet.sparrow.event.SorobanInitiatedEvent; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; +import com.sparrowwallet.sparrow.glyphfont.GlyphUtils; import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.soroban.SorobanServices; import com.sparrowwallet.sparrow.wallet.OptimizationStrategy; @@ -44,7 +45,6 @@ import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.util.Duration; -import org.controlsfx.glyphfont.FontAwesome; import org.controlsfx.glyphfont.Glyph; import org.controlsfx.tools.Platform; @@ -56,6 +56,8 @@ import java.util.*; import java.util.List; import java.util.stream.Collectors; +import static com.sparrowwallet.sparrow.glyphfont.GlyphUtils.*; + public class TransactionDiagram extends GridPane { private static final int MAX_UTXOS = 8; private static final int REDUCED_MAX_UTXOS = MAX_UTXOS - 2; @@ -686,19 +688,18 @@ public class TransactionDiagram extends GridPane { List outputNodes = new ArrayList<>(); for(Payment payment : displayedPayments) { - Glyph outputGlyph = getOutputGlyph(payment); + Glyph outputGlyph = GlyphUtils.getOutputGlyph(walletTx, payment); boolean labelledPayment = outputGlyph.getStyleClass().stream().anyMatch(style -> List.of("premix-icon", "badbank-icon", "whirlpoolfee-icon").contains(style)) || payment instanceof AdditionalPayment; - payment.setLabel(getOutputLabel(payment)); Label recipientLabel = new Label(payment.getLabel() == null || payment.getType() == Payment.Type.FAKE_MIX || payment.getType() == Payment.Type.MIX ? payment.getAddress().toString().substring(0, 8) + "..." : payment.getLabel(), outputGlyph); recipientLabel.getStyleClass().add("output-label"); recipientLabel.getStyleClass().add(labelledPayment ? "payment-label" : "recipient-label"); - Wallet toWallet = getToWallet(payment); + Wallet toWallet = walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment); WalletNode toNode = walletTx.getWallet() != null && !walletTx.getWallet().isBip47() ? walletTx.getAddressNodeMap().get(payment.getAddress()) : null; Wallet toBip47Wallet = getBip47SendWallet(payment); Tooltip recipientTooltip = new Tooltip((toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ") + getSatsValue(payment.getAmount()) + " sats to " + (payment instanceof AdditionalPayment ? (isExpanded() ? "\n" : "(click to expand)\n") + payment : (toWallet == null ? (payment.getLabel() == null ? (toNode != null ? toNode : (toBip47Wallet == null ? "external address" : toBip47Wallet.getDisplayName())) : payment.getLabel()) : toWallet.getFullDisplayName()) + "\n" + payment.getAddress().toString()) - + (isDuplicateAddress(payment) ? " (Duplicate)" : "")); + + (walletTx.isDuplicateAddress(payment) ? " (Duplicate)" : "")); recipientTooltip.getStyleClass().add("recipient-label"); recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY)); recipientTooltip.setShowDuration(Duration.INDEFINITE); @@ -928,42 +929,12 @@ public class TransactionDiagram extends GridPane { return spacer; } - private String getOutputLabel(Payment payment) { - if(payment.getLabel() != null) { - return payment.getLabel(); - } - - if(payment.getType() == Payment.Type.WHIRLPOOL_FEE) { - return "Whirlpool fee"; - } else if(walletTx.isPremixSend(payment)) { - int premixIndex = getOutputIndex(payment.getAddress(), payment.getAmount(), Collections.emptySet()) - 1; - return "Premix #" + premixIndex; - } else if(walletTx.isBadbankSend(payment)) { - return "Badbank change"; - } - - return null; - } - private int getOutputIndex(Address address, long amount, Collection seenIndexes) { List addressOutputs = walletTx.getTransaction().getOutputs().stream().filter(txOutput -> txOutput.getScript().getToAddress() != null).collect(Collectors.toList()); TransactionOutput output = addressOutputs.stream().filter(txOutput -> address.equals(txOutput.getScript().getToAddress()) && txOutput.getValue() == amount && !seenIndexes.contains(txOutput.getIndex())).findFirst().orElseThrow(); return addressOutputs.indexOf(output); } - Wallet getToWallet(Payment payment) { - for(Wallet openWallet : AppServices.get().getOpenWallets().keySet()) { - if(openWallet != walletTx.getWallet() && openWallet.isValid()) { - WalletNode addressNode = openWallet.getWalletAddresses().get(payment.getAddress()); - if(addressNode != null) { - return addressNode.getWallet(); - } - } - } - - return null; - } - private Wallet getBip47SendWallet(Payment payment) { if(walletTx.getWallet() != null) { for(Wallet childWallet : walletTx.getWallet().getChildWallets()) { @@ -981,164 +952,6 @@ public class TransactionDiagram extends GridPane { return null; } - public Glyph getOutputGlyph(Payment payment) { - if(payment.getType().equals(Payment.Type.MIX)) { - return getMixGlyph(); - } else if(payment.getType().equals(Payment.Type.FAKE_MIX)) { - return getFakeMixGlyph(); - } else if(walletTx.isConsolidationSend(payment)) { - return getConsolidationGlyph(); - } else if(walletTx.isPremixSend(payment)) { - return getPremixGlyph(); - } else if(walletTx.isBadbankSend(payment)) { - return getBadbankGlyph(); - } else if(payment.getType().equals(Payment.Type.WHIRLPOOL_FEE)) { - return getWhirlpoolFeeGlyph(); - } else if(payment instanceof AdditionalPayment) { - return ((AdditionalPayment)payment).getOutputGlyph(this); - } else if(getToWallet(payment) != null) { - return getDepositGlyph(); - } else if(isDuplicateAddress(payment)) { - return getPaymentWarningGlyph(); - } - - return getPaymentGlyph(); - } - - private boolean isDuplicateAddress(Payment payment) { - return walletTx.getPayments().stream().filter(p -> payment != p).anyMatch(p -> payment.getAddress() != null && payment.getAddress().equals(p.getAddress())); - } - - public static Glyph getExcludeGlyph() { - Glyph excludeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.TIMES_CIRCLE); - excludeGlyph.getStyleClass().add("exclude-utxo"); - excludeGlyph.setFontSize(12); - return excludeGlyph; - } - - public static Glyph getPaymentGlyph() { - Glyph paymentGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.SEND); - paymentGlyph.getStyleClass().add("payment-icon"); - paymentGlyph.setFontSize(12); - return paymentGlyph; - } - - public static Glyph getPaymentWarningGlyph() { - Glyph paymentWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_TRIANGLE); - paymentWarningGlyph.getStyleClass().add("payment-warning-icon"); - paymentWarningGlyph.setFontSize(12); - return paymentWarningGlyph; - } - - public static Glyph getConsolidationGlyph() { - Glyph consolidationGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.REPLY_ALL); - consolidationGlyph.getStyleClass().add("consolidation-icon"); - consolidationGlyph.setFontSize(12); - return consolidationGlyph; - } - - public static Glyph getDepositGlyph() { - Glyph depositGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.ARROW_DOWN); - depositGlyph.getStyleClass().add("deposit-icon"); - depositGlyph.setFontSize(12); - return depositGlyph; - } - - public static Glyph getPremixGlyph() { - Glyph premixGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.RANDOM); - premixGlyph.getStyleClass().add("premix-icon"); - premixGlyph.setFontSize(12); - return premixGlyph; - } - - public static Glyph getBadbankGlyph() { - Glyph badbankGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.BIOHAZARD); - badbankGlyph.getStyleClass().add("badbank-icon"); - badbankGlyph.setFontSize(12); - return badbankGlyph; - } - - public static Glyph getWhirlpoolFeeGlyph() { - Glyph whirlpoolFeeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING_WATER); - whirlpoolFeeGlyph.getStyleClass().add("whirlpoolfee-icon"); - whirlpoolFeeGlyph.setFontSize(12); - return whirlpoolFeeGlyph; - } - - public static Glyph getFakeMixGlyph() { - Glyph fakeMixGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.THEATER_MASKS); - fakeMixGlyph.getStyleClass().add("fakemix-icon"); - fakeMixGlyph.setFontSize(12); - return fakeMixGlyph; - } - - public static Glyph getTxoGlyph() { - return getChangeGlyph(); - } - - public static Glyph getMixGlyph() { - Glyph payjoinGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.RANDOM); - payjoinGlyph.getStyleClass().add("mix-icon"); - payjoinGlyph.setFontSize(12); - return payjoinGlyph; - } - - public static Glyph getChangeGlyph() { - Glyph changeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.COINS); - changeGlyph.getStyleClass().add("change-icon"); - changeGlyph.setFontSize(12); - return changeGlyph; - } - - public static Glyph getChangeWarningGlyph() { - Glyph changeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_TRIANGLE); - changeWarningGlyph.getStyleClass().add("change-warning-icon"); - changeWarningGlyph.setFontSize(12); - return changeWarningGlyph; - } - - public static Glyph getChangeReplaceGlyph() { - Glyph changeReplaceGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.ARROW_DOWN); - changeReplaceGlyph.getStyleClass().add("change-replace-icon"); - changeReplaceGlyph.setFontSize(12); - return changeReplaceGlyph; - } - - public Glyph getFeeGlyph() { - Glyph feeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING); - feeGlyph.getStyleClass().add("fee-icon"); - feeGlyph.setFontSize(12); - return feeGlyph; - } - - private Glyph getWarningGlyph() { - Glyph feeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_CIRCLE); - feeWarningGlyph.getStyleClass().add("fee-warning-icon"); - feeWarningGlyph.setFontSize(12); - return feeWarningGlyph; - } - - private Glyph getQuestionGlyph() { - Glyph feeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.QUESTION_CIRCLE); - feeWarningGlyph.getStyleClass().add("question-icon"); - feeWarningGlyph.setFontSize(12); - return feeWarningGlyph; - } - - private Glyph getLockGlyph() { - Glyph lockGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.LOCK); - lockGlyph.getStyleClass().add("lock-icon"); - lockGlyph.setFontSize(12); - return lockGlyph; - } - - private Glyph getUserGlyph() { - Glyph userGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.USER); - userGlyph.getStyleClass().add("user-icon"); - userGlyph.setFontSize(12); - return userGlyph; - } - private Glyph getUserAddGlyph() { Glyph userAddGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.USER_PLUS); userAddGlyph.getStyleClass().add("useradd-icon"); @@ -1295,7 +1108,7 @@ public class TransactionDiagram extends GridPane { } } - private static class AdditionalPayment extends Payment { + public static class AdditionalPayment extends Payment { private final List additionalPayments; public AdditionalPayment(List additionalPayments) { @@ -1303,10 +1116,10 @@ public class TransactionDiagram extends GridPane { this.additionalPayments = additionalPayments; } - public Glyph getOutputGlyph(TransactionDiagram transactionDiagram) { + public Glyph getOutputGlyph(WalletTransaction walletTx) { Glyph glyph = null; for(Payment payment : additionalPayments) { - Glyph paymentGlyph = transactionDiagram.getOutputGlyph(payment); + Glyph paymentGlyph = GlyphUtils.getOutputGlyph(walletTx, payment); if(glyph != null && !paymentGlyph.getStyleClass().equals(glyph.getStyleClass())) { return getPaymentGlyph(); } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java index e3f964bf..0629741e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java @@ -1,7 +1,9 @@ package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.wallet.*; +import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; +import com.sparrowwallet.sparrow.glyphfont.GlyphUtils; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.geometry.Pos; @@ -81,13 +83,13 @@ public class TransactionDiagramLabel extends HBox { List badbankOutputs = walletTx.getPayments().stream().filter(walletTx::isBadbankSend).collect(Collectors.toList()); List badbankOutputLabels = badbankOutputs.stream().map(payment -> getBadbankOutputLabel(transactionDiagram, payment)).collect(Collectors.toList()); outputLabels.addAll(badbankOutputLabels); - } else if(walletTx.getPayments().size() >= 5 && walletTx.getPayments().stream().mapToLong(Payment::getAmount).distinct().count() <= 1 + } else if(walletTx.getPayments().size() >= 5 && walletTx.getPayments().stream().mapToLong(Payment::getAmount).distinct().count() <= 1 && walletTx.getWallet() != null && walletTx.getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_PREMIX && walletTx.getPayments().stream().anyMatch(walletTx::isPostmixSend)) { OutputLabel mixOutputLabel = getMixOutputLabel(transactionDiagram, walletTx.getPayments()); if(mixOutputLabel != null) { outputLabels.add(mixOutputLabel); } - } else if(walletTx.getPayments().size() >= 5 && walletTx.getPayments().stream().mapToLong(Payment::getAmount).distinct().count() <= 1 + } else if(walletTx.getPayments().size() >= 5 && walletTx.getPayments().stream().mapToLong(Payment::getAmount).distinct().count() <= 1 && walletTx.getWallet() != null && walletTx.getWallet().getStandardAccountType() == StandardAccount.WHIRLPOOL_POSTMIX && walletTx.getPayments().stream().anyMatch(walletTx::isConsolidationSend)) { OutputLabel remixOutputLabel = getRemixOutputLabel(transactionDiagram, walletTx.getPayments()); if(remixOutputLabel != null) { @@ -142,7 +144,7 @@ public class TransactionDiagramLabel extends HBox { Payment premixOutput = premixOutputs.get(0); long total = premixOutputs.stream().mapToLong(Payment::getAmount).sum(); - Glyph glyph = transactionDiagram.getOutputGlyph(premixOutput); + Glyph glyph = GlyphUtils.getOutputGlyph(transactionDiagram.getWalletTransaction(), premixOutput); String text; if(premixOutputs.size() == 1) { text = "Premix transaction with 1 output of " + transactionDiagram.getSatsValue(premixOutput.getAmount()) + " sats"; @@ -155,7 +157,7 @@ public class TransactionDiagramLabel extends HBox { } private OutputLabel getBadbankOutputLabel(TransactionDiagram transactionDiagram, Payment payment) { - Glyph glyph = transactionDiagram.getOutputGlyph(payment); + Glyph glyph = GlyphUtils.getOutputGlyph(transactionDiagram.getWalletTransaction(), payment); String text = "Badbank change of " + transactionDiagram.getSatsValue(payment.getAmount()) + " sats to " + payment.getAddress().toString(); return getOutputLabel(glyph, text); @@ -164,7 +166,7 @@ public class TransactionDiagramLabel extends HBox { private OutputLabel getWhirlpoolFeeOutputLabel(TransactionDiagram transactionDiagram, Payment whirlpoolFee, List premixOutputs) { long total = premixOutputs.stream().mapToLong(Payment::getAmount).sum(); double feePercentage = (double)whirlpoolFee.getAmount() / (total - whirlpoolFee.getAmount()); - Glyph glyph = transactionDiagram.getOutputGlyph(whirlpoolFee); + Glyph glyph = GlyphUtils.getOutputGlyph(transactionDiagram.getWalletTransaction(), whirlpoolFee); String text = "Whirlpool fee of " + transactionDiagram.getSatsValue(whirlpoolFee.getAmount()) + " sats (" + String.format("%.2f", feePercentage * 100.0) + "% of total premix value)"; return getOutputLabel(glyph, text); @@ -177,7 +179,7 @@ public class TransactionDiagramLabel extends HBox { Payment remixOutput = mixOutputs.get(0); long total = mixOutputs.stream().mapToLong(Payment::getAmount).sum(); - Glyph glyph = TransactionDiagram.getPremixGlyph(); + Glyph glyph = GlyphUtils.getPremixGlyph(); String text = "Mix transaction with " + mixOutputs.size() + " outputs of " + transactionDiagram.getSatsValue(remixOutput.getAmount()) + " sats each (" + transactionDiagram.getSatsValue(total) + " sats)"; @@ -191,7 +193,7 @@ public class TransactionDiagramLabel extends HBox { Payment remixOutput = remixOutputs.get(0); long total = remixOutputs.stream().mapToLong(Payment::getAmount).sum(); - Glyph glyph = TransactionDiagram.getPremixGlyph(); + Glyph glyph = GlyphUtils.getPremixGlyph(); String text = "Remix transaction with " + remixOutputs.size() + " outputs of " + transactionDiagram.getSatsValue(remixOutput.getAmount()) + " sats each (" + transactionDiagram.getSatsValue(total) + " sats)"; @@ -200,10 +202,10 @@ public class TransactionDiagramLabel extends HBox { private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, Payment payment) { WalletTransaction walletTx = transactionDiagram.getWalletTransaction(); - Wallet toWallet = transactionDiagram.getToWallet(payment); + Wallet toWallet = walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment); WalletNode toNode = walletTx.getWallet() != null && !walletTx.getWallet().isBip47() ? walletTx.getAddressNodeMap().get(payment.getAddress()) : null; - Glyph glyph = transactionDiagram.getOutputGlyph(payment); + Glyph glyph = GlyphUtils.getOutputGlyph(transactionDiagram.getWalletTransaction(), payment); String text = (toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ") + transactionDiagram.getSatsValue(payment.getAmount()) + " sats to " + payment.getAddress().toString(); return getOutputLabel(glyph, text); @@ -212,7 +214,7 @@ public class TransactionDiagramLabel extends HBox { private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, Map.Entry changeEntry) { WalletTransaction walletTx = transactionDiagram.getWalletTransaction(); - Glyph glyph = TransactionDiagram.getChangeGlyph(); + Glyph glyph = GlyphUtils.getChangeGlyph(); String text = "Change of " + transactionDiagram.getSatsValue(changeEntry.getValue()) + " sats to " + walletTx.getChangeAddress(changeEntry.getKey()).toString(); return getOutputLabel(glyph, text); @@ -224,7 +226,7 @@ public class TransactionDiagramLabel extends HBox { return null; } - Glyph glyph = transactionDiagram.getFeeGlyph(); + Glyph glyph = GlyphUtils.getFeeGlyph(); String text = "Fee of " + transactionDiagram.getSatsValue(walletTx.getFee()) + " sats (" + String.format("%.2f", walletTx.getFeePercentage() * 100.0) + "%)"; return getOutputLabel(glyph, text); diff --git a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java index 2b83a2b9..5616017c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java +++ b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java @@ -51,8 +51,10 @@ public class FontAwesome5 extends GlyphFont { LINK('\uf0c1'), LOCK('\uf023'), LOCK_OPEN('\uf3c1'), + LONG_ARROW_ALT_RIGHT('\uf30b'), MAGNIFYING_GLASS_PLUS('\uf00e'), MAGNIFYING_GLASS_MINUS('\uf010'), + MICROCHIP('\uf2db'), MINUS_CIRCLE('\uf056'), PEN_FANCY('\uf5ac'), PLUS('\uf067'), diff --git a/src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java b/src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java new file mode 100644 index 00000000..8ff16602 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java @@ -0,0 +1,178 @@ +package com.sparrowwallet.sparrow.glyphfont; + +import com.sparrowwallet.drongo.wallet.Payment; +import com.sparrowwallet.drongo.wallet.WalletTransaction; +import com.sparrowwallet.sparrow.AppServices; +import com.sparrowwallet.sparrow.control.TransactionDiagram; +import org.controlsfx.glyphfont.FontAwesome; +import org.controlsfx.glyphfont.Glyph; + +public class GlyphUtils { + public static Glyph getOutputGlyph(WalletTransaction walletTx, Payment payment) { + if(payment.getType().equals(Payment.Type.MIX)) { + return getMixGlyph(); + } else if(payment.getType().equals(Payment.Type.FAKE_MIX)) { + return getFakeMixGlyph(); + } else if(walletTx.isConsolidationSend(payment)) { + return getConsolidationGlyph(); + } else if(walletTx.isPremixSend(payment)) { + return getPremixGlyph(); + } else if(walletTx.isBadbankSend(payment)) { + return getBadbankGlyph(); + } else if(payment.getType().equals(Payment.Type.WHIRLPOOL_FEE)) { + return getWhirlpoolFeeGlyph(); + } else if(payment instanceof TransactionDiagram.AdditionalPayment) { + return ((TransactionDiagram.AdditionalPayment)payment).getOutputGlyph(walletTx); + } else if(walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment) != null) { + return getDepositGlyph(); + } else if(walletTx.isDuplicateAddress(payment)) { + return getPaymentWarningGlyph(); + } + + return getPaymentGlyph(); + } + + public static Glyph getPaymentGlyph() { + Glyph paymentGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.SEND); + paymentGlyph.getStyleClass().add("payment-icon"); + paymentGlyph.setFontSize(12); + return paymentGlyph; + } + + public static Glyph getPaymentWarningGlyph() { + Glyph paymentWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_TRIANGLE); + paymentWarningGlyph.getStyleClass().add("payment-warning-icon"); + paymentWarningGlyph.setFontSize(12); + return paymentWarningGlyph; + } + + public static Glyph getConsolidationGlyph() { + Glyph consolidationGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.REPLY_ALL); + consolidationGlyph.getStyleClass().add("consolidation-icon"); + consolidationGlyph.setFontSize(12); + return consolidationGlyph; + } + + public static Glyph getDepositGlyph() { + Glyph depositGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.ARROW_DOWN); + depositGlyph.getStyleClass().add("deposit-icon"); + depositGlyph.setFontSize(12); + return depositGlyph; + } + + public static Glyph getPremixGlyph() { + Glyph premixGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.RANDOM); + premixGlyph.getStyleClass().add("premix-icon"); + premixGlyph.setFontSize(12); + return premixGlyph; + } + + public static Glyph getBadbankGlyph() { + Glyph badbankGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.BIOHAZARD); + badbankGlyph.getStyleClass().add("badbank-icon"); + badbankGlyph.setFontSize(12); + return badbankGlyph; + } + + public static Glyph getWhirlpoolFeeGlyph() { + Glyph whirlpoolFeeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING_WATER); + whirlpoolFeeGlyph.getStyleClass().add("whirlpoolfee-icon"); + whirlpoolFeeGlyph.setFontSize(12); + return whirlpoolFeeGlyph; + } + + public static Glyph getFakeMixGlyph() { + Glyph fakeMixGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.THEATER_MASKS); + fakeMixGlyph.getStyleClass().add("fakemix-icon"); + fakeMixGlyph.setFontSize(12); + return fakeMixGlyph; + } + + public static Glyph getTxoGlyph() { + return getChangeGlyph(); + } + + public static Glyph getMixGlyph() { + Glyph payjoinGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.RANDOM); + payjoinGlyph.getStyleClass().add("mix-icon"); + payjoinGlyph.setFontSize(12); + return payjoinGlyph; + } + + public static Glyph getExternalInputGlyph() { + Glyph externalGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.LONG_ARROW_ALT_RIGHT); + externalGlyph.getStyleClass().add("external-input-icon"); + externalGlyph.setFontSize(12); + return externalGlyph; + } + + public static Glyph getExcludeGlyph() { + Glyph excludeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.TIMES_CIRCLE); + excludeGlyph.getStyleClass().add("exclude-utxo"); + excludeGlyph.setFontSize(12); + return excludeGlyph; + } + + public static Glyph getChangeGlyph() { + Glyph changeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.COINS); + changeGlyph.getStyleClass().add("change-icon"); + changeGlyph.setFontSize(12); + return changeGlyph; + } + + public static Glyph getChangeWarningGlyph() { + Glyph changeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_TRIANGLE); + changeWarningGlyph.getStyleClass().add("change-warning-icon"); + changeWarningGlyph.setFontSize(12); + return changeWarningGlyph; + } + + public static Glyph getChangeReplaceGlyph() { + Glyph changeReplaceGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.ARROW_DOWN); + changeReplaceGlyph.getStyleClass().add("change-replace-icon"); + changeReplaceGlyph.setFontSize(12); + return changeReplaceGlyph; + } + + public static Glyph getFeeGlyph() { + Glyph feeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING); + feeGlyph.getStyleClass().add("fee-icon"); + feeGlyph.setFontSize(12); + return feeGlyph; + } + + public static Glyph getWarningGlyph() { + Glyph feeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_CIRCLE); + feeWarningGlyph.getStyleClass().add("fee-warning-icon"); + feeWarningGlyph.setFontSize(12); + return feeWarningGlyph; + } + + public static Glyph getQuestionGlyph() { + Glyph feeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.QUESTION_CIRCLE); + feeWarningGlyph.getStyleClass().add("question-icon"); + feeWarningGlyph.setFontSize(12); + return feeWarningGlyph; + } + + public static Glyph getLockGlyph() { + Glyph lockGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.LOCK); + lockGlyph.getStyleClass().add("lock-icon"); + lockGlyph.setFontSize(12); + return lockGlyph; + } + + public static Glyph getUserGlyph() { + Glyph userGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.USER); + userGlyph.getStyleClass().add("user-icon"); + userGlyph.setFontSize(12); + return userGlyph; + } + + public static Glyph getOpcodeGlyph() { + Glyph userGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.MICROCHIP); + userGlyph.getStyleClass().add("opcode-icon"); + userGlyph.setFontSize(12); + return userGlyph; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java index 9df562a3..18644b8d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java @@ -437,8 +437,11 @@ public class HeadersController extends TransactionFormController implements Init updateFee(feeAmt); } + headersForm.walletTransactionProperty().addListener((observable, oldValue, walletTransaction) -> { + transactionDiagram.update(walletTransaction); + }); transactionDiagram.labelProperty().set(transactionDiagramLabel); - transactionDiagram.update(getWalletTransaction(headersForm.getInputTransactions())); + headersForm.setWalletTransaction(getWalletTransaction(headersForm.getInputTransactions())); blockchainForm.managedProperty().bind(blockchainForm.visibleProperty()); @@ -1334,7 +1337,7 @@ public class HeadersController extends TransactionFormController implements Init if(headersForm.getInputTransactions() != null) { allFetchedInputTransactions.putAll(headersForm.getInputTransactions()); } - transactionDiagram.update(getWalletTransaction(allFetchedInputTransactions)); + headersForm.setWalletTransaction(getWalletTransaction(allFetchedInputTransactions)); } } } @@ -1500,7 +1503,7 @@ public class HeadersController extends TransactionFormController implements Init updateType(); updateSize(); updateFee(headersForm.getPsbt().getFee()); - transactionDiagram.update(getWalletTransaction(headersForm.getInputTransactions())); + headersForm.setWalletTransaction(getWalletTransaction(headersForm.getInputTransactions())); } } @@ -1597,7 +1600,7 @@ public class HeadersController extends TransactionFormController implements Init public void psbtReordered(PSBTReorderedEvent event) { if(event.getPsbt().equals(headersForm.getPsbt())) { updateTxId(); - transactionDiagram.update(getWalletTransaction(headersForm.getInputTransactions())); + headersForm.setWalletTransaction(getWalletTransaction(headersForm.getInputTransactions())); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java index b12a7dc8..6ba500dc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java @@ -12,6 +12,7 @@ import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.*; import com.sparrowwallet.sparrow.event.*; +import com.sparrowwallet.sparrow.glyphfont.GlyphUtils; import javafx.collections.MapChangeListener; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -124,7 +125,7 @@ public class InputController extends TransactionFormController implements Initia inputForm.signingWalletProperty().addListener((observable, oldValue, signingWallet) -> { updateInputLegendFromWallet(txInput, signingWallet); }); - updateInputLegendFromWallet(txInput, inputForm.getSigningWallet()); + updateInputLegendFromWallet(txInput, inputForm.getWallet()); initializeInputFields(txInput, psbtInput); initializeScriptFields(txInput, psbtInput); @@ -142,19 +143,19 @@ public class InputController extends TransactionFormController implements Initia return "Input #" + txInput.getIndex(); } - private void updateInputLegendFromWallet(TransactionInput txInput, Wallet signingWallet) { + private void updateInputLegendFromWallet(TransactionInput txInput, Wallet wallet) { String baseText = getLegendText(txInput); - if(signingWallet != null) { + if(wallet != null) { if(inputForm.isWalletTxo()) { - inputFieldset.setText(baseText + " from " + signingWallet.getFullDisplayName()); - inputFieldset.setIcon(TransactionDiagram.getTxoGlyph()); + inputFieldset.setText(baseText + " from " + wallet.getFullDisplayName()); + inputFieldset.setIcon(GlyphUtils.getTxoGlyph()); } else { - inputFieldset.setText(baseText + " - External"); - inputFieldset.setIcon(TransactionDiagram.getMixGlyph()); + inputFieldset.setText(baseText + (txInput.isCoinBase() ? " - Coinbase" : " - External")); + inputFieldset.setIcon(GlyphUtils.getMixGlyph()); } } else { - inputFieldset.setText(baseText); - inputFieldset.setIcon(null); + inputFieldset.setText(baseText + (txInput.isCoinBase() ? " - Coinbase" : " - External")); + inputFieldset.setIcon(GlyphUtils.getExternalInputGlyph()); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java index f206963e..273d70c3 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/InputForm.java @@ -1,14 +1,20 @@ package com.sparrowwallet.sparrow.transaction; import com.sparrowwallet.drongo.protocol.TransactionInput; +import com.sparrowwallet.drongo.protocol.TransactionOutPoint; import com.sparrowwallet.drongo.protocol.TransactionOutput; import com.sparrowwallet.drongo.psbt.PSBTInput; import com.sparrowwallet.drongo.wallet.BlockTransaction; +import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; +import com.sparrowwallet.sparrow.glyphfont.GlyphUtils; import com.sparrowwallet.sparrow.net.ElectrumServer; import javafx.fxml.FXMLLoader; import javafx.scene.Node; +import javafx.scene.control.Label; +import org.controlsfx.glyphfont.Glyph; import java.io.IOException; +import java.util.Optional; public class InputForm extends IndexedTransactionForm { public InputForm(TransactionData txdata, PSBTInput psbtInput) { @@ -48,7 +54,7 @@ public class InputForm extends IndexedTransactionForm { public boolean isWalletTxo() { TransactionInput txInput = getTransactionInput(); - return getSigningWallet() != null && getSigningWallet().isWalletTxo(txInput); + return getWallet() != null && getWallet().isWalletTxo(txInput); } @Override @@ -67,6 +73,24 @@ public class InputForm extends IndexedTransactionForm { } public String toString() { - return "Input #" + getIndex(); + TransactionOutPoint outPoint = getTransactionInput().getOutpoint(); + return outPoint.getHash().toString().substring(0, 8) + "..:" + outPoint.getIndex(); + } + + @Override + public Label getLabel() { + if(getWalletTransaction() != null) { + TransactionOutPoint outPoint = getTransactionInput().getOutpoint(); + Optional optRef = getWalletTransaction().getSelectedUtxos().keySet().stream() + .filter(txo -> txo.getHash().equals(outPoint.getHash()) && txo.getIndex() == outPoint.getIndex()).findFirst(); + Glyph inputGlyph = isWalletTxo() ? GlyphUtils.getTxoGlyph() : (getWallet() != null ? GlyphUtils.getMixGlyph() : GlyphUtils.getExternalInputGlyph()); + if(optRef.isPresent() && optRef.get().getLabel() != null) { + return new Label(optRef.get().getLabel(), inputGlyph); + } else { + return new Label(toString(), inputGlyph); + } + } + + return super.getLabel(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java index 602ae0ae..b025d9bd 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java @@ -5,8 +5,8 @@ import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.protocol.NonStandardScriptException; import com.sparrowwallet.drongo.protocol.TransactionInput; import com.sparrowwallet.drongo.protocol.TransactionOutput; -import com.sparrowwallet.drongo.wallet.BlockTransaction; -import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.drongo.wallet.*; +import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.*; import com.sparrowwallet.sparrow.event.PSBTReorderedEvent; @@ -66,7 +66,10 @@ public class OutputController extends TransactionFormController implements Initi outputForm.signingWalletProperty().addListener((observable, oldValue, signingWallet) -> { updateOutputLegendFromWallet(txOutput, signingWallet); }); - updateOutputLegendFromWallet(txOutput, outputForm.getSigningWallet()); + outputForm.walletTransactionProperty().addListener((observable, oldValue, walletTransaction) -> { + updateOutputLegendFromWallet(txOutput, walletTransaction != null ? walletTransaction.getWallet() : null); + }); + updateOutputLegendFromWallet(txOutput, outputForm.getWallet()); value.setValue(txOutput.getValue()); to.setVisible(false); @@ -103,23 +106,40 @@ public class OutputController extends TransactionFormController implements Initi return "Output #" + txOutput.getIndex(); } - private void updateOutputLegendFromWallet(TransactionOutput txOutput, Wallet signingWallet) { + private void updateOutputLegendFromWallet(TransactionOutput txOutput, Wallet wallet) { String baseText = getLegendText(txOutput); - if(signingWallet != null) { + WalletTransaction walletTx = outputForm.getWalletTransaction(); + if(walletTx != null) { + List outputs = walletTx.getOutputs(); + if(outputForm.getIndex() < outputs.size()) { + WalletTransaction.Output output = outputs.get(outputForm.getIndex()); + if(output instanceof WalletTransaction.NonAddressOutput) { + outputFieldset.setText(baseText); + } else if(output instanceof WalletTransaction.PaymentOutput paymentOutput) { + Payment payment = paymentOutput.getPayment(); + Wallet toWallet = walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment); + WalletNode toNode = walletTx.getWallet() != null && !walletTx.getWallet().isBip47() ? walletTx.getAddressNodeMap().get(payment.getAddress()) : null; + outputFieldset.setText(baseText + (toWallet == null ? (toNode != null ? " - Consolidation" : " - Payment") : " - Received to " + toWallet.getFullDisplayName())); + } else if(output instanceof WalletTransaction.ChangeOutput changeOutput) { + outputFieldset.setText(baseText + " - Change to " + changeOutput.getWalletNode().toString()); + } else { + outputFieldset.setText(baseText); + } + } else { + outputFieldset.setText(baseText); + } + } else if(wallet != null) { if(outputForm.isWalletChange()) { outputFieldset.setText(baseText + " - Change"); - outputFieldset.setIcon(TransactionDiagram.getChangeGlyph()); } else if(outputForm.isWalletConsolidation()) { outputFieldset.setText(baseText + " - Consolidation"); - outputFieldset.setIcon(TransactionDiagram.getConsolidationGlyph()); } else { outputFieldset.setText(baseText + " - Payment"); - outputFieldset.setIcon(TransactionDiagram.getPaymentGlyph()); } } else { outputFieldset.setText(baseText); - outputFieldset.setIcon(null); } + outputFieldset.setIcon(outputForm.getLabel().getGraphic()); } private void updateSpent(List outputTransactions) { diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java index 18974462..4fffe6e2 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java @@ -1,12 +1,20 @@ package com.sparrowwallet.sparrow.transaction; import com.sparrowwallet.drongo.KeyPurpose; +import com.sparrowwallet.drongo.address.Address; +import com.sparrowwallet.drongo.protocol.ScriptChunk; +import com.sparrowwallet.drongo.protocol.ScriptOpCodes; import com.sparrowwallet.drongo.protocol.TransactionOutput; import com.sparrowwallet.drongo.psbt.PSBTOutput; +import com.sparrowwallet.drongo.wallet.Payment; +import com.sparrowwallet.drongo.wallet.WalletTransaction; +import com.sparrowwallet.sparrow.glyphfont.GlyphUtils; import javafx.fxml.FXMLLoader; import javafx.scene.Node; +import javafx.scene.control.Label; import java.io.IOException; +import java.util.*; public class OutputForm extends IndexedTransactionForm { public OutputForm(TransactionData txdata, PSBTOutput psbtOutput) { @@ -26,15 +34,15 @@ public class OutputForm extends IndexedTransactionForm { } public boolean isWalletConsolidation() { - return (getSigningWallet() != null && getSigningWallet().getWalletOutputScripts(KeyPurpose.RECEIVE).containsKey(getTransactionOutput().getScript())); + return (getWallet() != null && getWallet().getWalletOutputScripts(KeyPurpose.RECEIVE).containsKey(getTransactionOutput().getScript())); } public boolean isWalletChange() { - return (getSigningWallet() != null && getSigningWallet().getWalletOutputScripts(getSigningWallet().getChangeKeyPurpose()).containsKey(getTransactionOutput().getScript())); + return (getWallet() != null && getWallet().getWalletOutputScripts(getWallet().getChangeKeyPurpose()).containsKey(getTransactionOutput().getScript())); } public boolean isWalletPayment() { - return getSigningWallet() != null; + return getWallet() != null; } @Override @@ -53,6 +61,33 @@ public class OutputForm extends IndexedTransactionForm { } public String toString() { - return "Output #" + getIndex(); + Address address = getTransactionOutput().getScript().getToAddress(); + return address != null ? address.toString() : "Output #" + getIndex(); + } + + @Override + public Label getLabel() { + if(getWalletTransaction() != null) { + List outputs = getWalletTransaction().getOutputs(); + if(getIndex() < outputs.size()) { + WalletTransaction.Output output = outputs.get(getIndex()); + if(output instanceof WalletTransaction.NonAddressOutput) { + List chunks = output.getTransactionOutput().getScript().getChunks(); + if(!chunks.isEmpty() && chunks.get(0).isOpCode() && chunks.get(0).getOpcode() == ScriptOpCodes.OP_RETURN) { + return new Label(chunks.get(0).toString(), GlyphUtils.getOpcodeGlyph()); + } else { + return new Label("Output #" + getIndex(), GlyphUtils.getOpcodeGlyph()); + } + } else if(output instanceof WalletTransaction.PaymentOutput paymentOutput) { + Payment payment = paymentOutput.getPayment(); + return new Label(payment.getLabel() != null && payment.getType() != Payment.Type.FAKE_MIX && payment.getType() != Payment.Type.MIX ? payment.getLabel() : payment.getAddress().toString(), + GlyphUtils.getOutputGlyph(getWalletTransaction(), payment)); + } else if(output instanceof WalletTransaction.ChangeOutput changeOutput) { + return new Label(changeOutput.getWalletNode().getAddress().toString(), GlyphUtils.getChangeGlyph()); + } + } + } + + return super.getLabel(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java index 9b71f396..17671d22 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java @@ -10,7 +10,6 @@ import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.TransactionTabData; -import com.sparrowwallet.sparrow.control.TransactionDiagram; import com.sparrowwallet.sparrow.control.TransactionHexArea; import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.io.Config; @@ -20,6 +19,7 @@ import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.Parent; +import javafx.scene.control.Label; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; @@ -158,28 +158,11 @@ public class TransactionController implements Initializable { setContextMenu(null); if(form != null) { - setText(form.toString()); + Label label = form.getLabel(); + label.setMaxWidth(100); + setGraphic(label); if(form.getSigningWallet() != null) { - if(form instanceof InputForm) { - InputForm inputForm = (InputForm)form; - if(inputForm.isWalletTxo()) { - setGraphic(TransactionDiagram.getTxoGlyph()); - } else { - setGraphic(TransactionDiagram.getMixGlyph()); - } - } - if(form instanceof OutputForm) { - OutputForm outputForm = (OutputForm)form; - if(outputForm.isWalletChange()) { - setGraphic(TransactionDiagram.getChangeGlyph()); - } else if(outputForm.isWalletConsolidation()) { - setGraphic(TransactionDiagram.getConsolidationGlyph()); - } else { - setGraphic(TransactionDiagram.getPaymentGlyph()); - } - } - setOnDragDetected(null); setOnDragOver(null); setOnDragDropped(null); @@ -196,6 +179,10 @@ public class TransactionController implements Initializable { txtree.refresh(); }); + txdata.walletTransactionProperty().addListener((observable, oldValue, newValue) -> { + txtree.refresh(); + }); + txtree.getSelectionModel().selectedItemProperty().addListener((observable, old_val, selectedItem) -> { TransactionForm transactionForm = selectedItem.getValue(); if(transactionForm instanceof PageForm) { diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java index 11a2b393..71044841 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionData.java @@ -3,10 +3,7 @@ package com.sparrowwallet.sparrow.transaction; import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.protocol.*; import com.sparrowwallet.drongo.psbt.PSBT; -import com.sparrowwallet.drongo.wallet.BlockTransaction; -import com.sparrowwallet.drongo.wallet.Keystore; -import com.sparrowwallet.drongo.wallet.Wallet; -import com.sparrowwallet.drongo.wallet.WalletNode; +import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.io.Storage; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; @@ -30,6 +27,7 @@ public class TransactionData { private final ObservableMap availableWallets = FXCollections.observableHashMap(); private final SimpleObjectProperty signingWallet = new SimpleObjectProperty<>(this, "signingWallet", null); private final ObservableMap signatureKeystoreMap = FXCollections.observableMap(new LinkedHashMap<>()); + private final SimpleObjectProperty walletTransaction = new SimpleObjectProperty<>(this, "walletTransaction", null); public TransactionData(String name, PSBT psbt) { this(name, psbt.getTransaction()); @@ -179,4 +177,20 @@ public class TransactionData { return signingWalletNodes; } + + public WalletTransaction getWalletTransaction() { + return walletTransaction.get(); + } + + public SimpleObjectProperty walletTransactionProperty() { + return walletTransaction; + } + + public void setWalletTransaction(WalletTransaction walletTransaction) { + this.walletTransaction.set(walletTransaction); + } + + public Wallet getWallet() { + return getSigningWallet() != null ? getSigningWallet() : (getWalletTransaction() != null ? getWalletTransaction().getWallet() : null); + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java index 385fbb13..d8cdb422 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionForm.java @@ -4,14 +4,12 @@ import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.TransactionSignature; import com.sparrowwallet.drongo.psbt.PSBT; -import com.sparrowwallet.drongo.wallet.BlockTransaction; -import com.sparrowwallet.drongo.wallet.Keystore; -import com.sparrowwallet.drongo.wallet.Wallet; -import com.sparrowwallet.drongo.wallet.WalletNode; +import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.io.Storage; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.ObservableMap; import javafx.scene.Node; +import javafx.scene.control.Label; import java.io.IOException; import java.util.Collection; @@ -98,6 +96,22 @@ public abstract class TransactionForm { return txdata.getSigningWalletNodes(); } + public WalletTransaction getWalletTransaction() { + return txdata.getWalletTransaction(); + } + + public SimpleObjectProperty walletTransactionProperty() { + return txdata.walletTransactionProperty(); + } + + public void setWalletTransaction(WalletTransaction walletTransaction) { + txdata.setWalletTransaction(walletTransaction); + } + + public Wallet getWallet() { + return txdata.getWallet(); + } + public boolean isEditable() { if(getBlockTransaction() != null) { return false; @@ -120,4 +134,8 @@ public abstract class TransactionForm { public abstract Node getContents() throws IOException; public abstract TransactionView getView(); + + public Label getLabel() { + return new Label(toString()); + } } diff --git a/src/main/resources/com/sparrowwallet/sparrow/transaction/transaction.css b/src/main/resources/com/sparrowwallet/sparrow/transaction/transaction.css index c8d91aa5..b0c20428 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/transaction/transaction.css +++ b/src/main/resources/com/sparrowwallet/sparrow/transaction/transaction.css @@ -30,4 +30,8 @@ .color-7 { -fx-fill: #986801 } .color-8 { -fx-fill: #000000 } -.color-grey { -fx-fill: #e5e5e6 } \ No newline at end of file +.color-grey { -fx-fill: #e5e5e6 } + +.change-warning-icon, .payment-warning-icon { + -fx-text-fill: rgb(238, 210, 2); +} \ No newline at end of file