From 6ac294920ec6f09761763e37d1e20c9256e8fb30 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 30 Nov 2022 11:12:01 +0200 Subject: [PATCH] improve encapsulation and binding lifecycle of cell confirmation listeners --- .../sparrow/control/CoinCell.java | 83 ++++++++++++++++--- .../control/ConfirmationsListener.java | 7 ++ .../sparrow/control/EntryCell.java | 36 ++++++-- .../sparrow/control/LabelCell.java | 22 ++++- .../sparrow/wallet/TransactionEntry.java | 11 --- 5 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/control/ConfirmationsListener.java diff --git a/src/main/java/com/sparrowwallet/sparrow/control/CoinCell.java b/src/main/java/com/sparrowwallet/sparrow/control/CoinCell.java index fcaf3012..1d681a53 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/CoinCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/CoinCell.java @@ -2,11 +2,14 @@ package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.protocol.Transaction; +import com.sparrowwallet.drongo.wallet.BlockTransactionHash; import com.sparrowwallet.sparrow.UnitFormat; import com.sparrowwallet.sparrow.wallet.Entry; import com.sparrowwallet.sparrow.wallet.HashIndexEntry; import com.sparrowwallet.sparrow.wallet.TransactionEntry; import com.sparrowwallet.sparrow.wallet.UtxoEntry; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; import javafx.scene.control.ContentDisplay; import javafx.scene.control.Tooltip; import javafx.scene.control.TreeTableCell; @@ -16,12 +19,14 @@ import org.controlsfx.tools.Platform; import java.text.DecimalFormat; -class CoinCell extends TreeTableCell { - private final Tooltip tooltip; +class CoinCell extends TreeTableCell implements ConfirmationsListener { + private final CoinTooltip tooltip; + + private IntegerProperty confirmationsProperty; public CoinCell() { super(); - tooltip = new Tooltip(); + tooltip = new CoinTooltip(); tooltip.setShowDelay(Duration.millis(500)); getStyleClass().add("coin-cell"); if(Platform.getCurrent() == Platform.OSX) { @@ -50,21 +55,16 @@ class CoinCell extends TreeTableCell { final String btcValue = decimalFormat.format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN); if(unit.equals(BitcoinUnit.BTC)) { - tooltip.setText(satsValue + " " + BitcoinUnit.SATOSHIS.getLabel()); + tooltip.setValue(satsValue + " " + BitcoinUnit.SATOSHIS.getLabel()); setText(btcValue); } else { - tooltip.setText(btcValue + " " + BitcoinUnit.BTC.getLabel()); + tooltip.setValue(btcValue + " " + BitcoinUnit.BTC.getLabel()); setText(satsValue); } setTooltip(tooltip); - String tooltipValue = tooltip.getText(); if(entry instanceof TransactionEntry transactionEntry) { - tooltip.setText(tooltipValue + " (" + transactionEntry.getConfirmationsDescription() + ")"); - - transactionEntry.confirmationsProperty().addListener((observable, oldValue, newValue) -> { - tooltip.setText(tooltipValue + " (" + transactionEntry.getConfirmationsDescription() + ")"); - }); + tooltip.showConfirmations(transactionEntry.confirmationsProperty()); if(transactionEntry.isConfirming()) { ConfirmationProgressIndicator arc = new ConfirmationProgressIndicator(transactionEntry.getConfirmations()); @@ -94,4 +94,65 @@ class CoinCell extends TreeTableCell { } } } + + @Override + public IntegerProperty getConfirmationsProperty() { + if(confirmationsProperty == null) { + confirmationsProperty = new SimpleIntegerProperty(); + confirmationsProperty.addListener((observable, oldValue, newValue) -> { + if(newValue.intValue() >= BlockTransactionHash.BLOCKS_TO_CONFIRM) { + getStyleClass().remove("confirming"); + confirmationsProperty.unbind(); + } + }); + } + + return confirmationsProperty; + } + + private static final class CoinTooltip extends Tooltip { + private final IntegerProperty confirmationsProperty = new SimpleIntegerProperty(); + private boolean showConfirmations; + private String value; + + public void setValue(String value) { + this.value = value; + setTooltipText(); + } + + public void showConfirmations(IntegerProperty txEntryConfirmationsProperty) { + showConfirmations = true; + + int confirmations = txEntryConfirmationsProperty.get(); + if(confirmations < BlockTransactionHash.BLOCKS_TO_FULLY_CONFIRM) { + confirmationsProperty.bind(txEntryConfirmationsProperty); + confirmationsProperty.addListener((observable, oldValue, newValue) -> { + setTooltipText(); + if(newValue.intValue() >= BlockTransactionHash.BLOCKS_TO_FULLY_CONFIRM) { + confirmationsProperty.unbind(); + } + }); + } else { + confirmationsProperty.unbind(); + confirmationsProperty.set(confirmations); + } + + setTooltipText(); + } + + private void setTooltipText() { + setText(value + (showConfirmations ? " (" + getConfirmationsDescription() + ")" : "")); + } + + public String getConfirmationsDescription() { + int confirmations = confirmationsProperty.get(); + if(confirmations == 0) { + return "Unconfirmed in mempool"; + } else if(confirmations < BlockTransactionHash.BLOCKS_TO_FULLY_CONFIRM) { + return confirmations + " confirmation" + (confirmations == 1 ? "" : "s"); + } else { + return BlockTransactionHash.BLOCKS_TO_FULLY_CONFIRM + "+ confirmations"; + } + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/ConfirmationsListener.java b/src/main/java/com/sparrowwallet/sparrow/control/ConfirmationsListener.java new file mode 100644 index 00000000..f03e259d --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/control/ConfirmationsListener.java @@ -0,0 +1,7 @@ +package com.sparrowwallet.sparrow.control; + +import javafx.beans.property.IntegerProperty; + +public interface ConfirmationsListener { + IntegerProperty getConfirmationsProperty(); +} diff --git a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java index 3ed5c841..65c0eda7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java @@ -12,6 +12,8 @@ import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.wallet.*; import javafx.application.Platform; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; import javafx.geometry.Pos; import javafx.scene.control.*; import javafx.scene.input.Clipboard; @@ -30,7 +32,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -public class EntryCell extends TreeTableCell { +public class EntryCell extends TreeTableCell implements ConfirmationsListener { private static final Logger log = LoggerFactory.getLogger(EntryCell.class); public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm"); @@ -38,6 +40,8 @@ public class EntryCell extends TreeTableCell { private static EntryCell lastCell; + private IntegerProperty confirmationsProperty; + public EntryCell() { super(); setAlignment(Pos.CENTER_LEFT); @@ -189,6 +193,21 @@ public class EntryCell extends TreeTableCell { } } + @Override + public IntegerProperty getConfirmationsProperty() { + if(confirmationsProperty == null) { + confirmationsProperty = new SimpleIntegerProperty(); + confirmationsProperty.addListener((observable, oldValue, newValue) -> { + if(newValue.intValue() >= BlockTransactionHash.BLOCKS_TO_CONFIRM) { + getStyleClass().remove("confirming"); + confirmationsProperty.unbind(); + } + }); + } + + return confirmationsProperty; + } + private static void increaseFee(TransactionEntry transactionEntry, boolean cancelTransaction) { BlockTransaction blockTransaction = transactionEntry.getBlockTransaction(); Map walletTxos = transactionEntry.getWallet().getWalletTxos(); @@ -722,13 +741,14 @@ public class EntryCell extends TreeTableCell { if(entry instanceof TransactionEntry) { cell.getStyleClass().add("transaction-row"); TransactionEntry transactionEntry = (TransactionEntry)entry; - if(transactionEntry.isConfirming()) { - cell.getStyleClass().add("confirming"); - transactionEntry.confirmationsProperty().addListener((observable, oldValue, newValue) -> { - if(!transactionEntry.isConfirming()) { - cell.getStyleClass().remove("confirming"); - } - }); + + if(cell instanceof ConfirmationsListener confirmationsListener) { + if(transactionEntry.isConfirming()) { + cell.getStyleClass().add("confirming"); + confirmationsListener.getConfirmationsProperty().bind(transactionEntry.confirmationsProperty()); + } else { + confirmationsListener.getConfirmationsProperty().unbind(); + } } } else if(entry instanceof NodeEntry) { cell.getStyleClass().add("node-row"); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/LabelCell.java b/src/main/java/com/sparrowwallet/sparrow/control/LabelCell.java index 93d765ac..f51cc541 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/LabelCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/LabelCell.java @@ -1,6 +1,9 @@ package com.sparrowwallet.sparrow.control; +import com.sparrowwallet.drongo.wallet.BlockTransactionHash; import com.sparrowwallet.sparrow.wallet.Entry; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; import javafx.event.Event; import javafx.scene.control.*; import javafx.scene.control.cell.TextFieldTreeTableCell; @@ -13,9 +16,11 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Field; -class LabelCell extends TextFieldTreeTableCell { +class LabelCell extends TextFieldTreeTableCell implements ConfirmationsListener { private static final Logger log = LoggerFactory.getLogger(LabelCell.class); + private IntegerProperty confirmationsProperty; + public LabelCell() { super(new DefaultStringConverter()); getStyleClass().add("label-cell"); @@ -81,6 +86,21 @@ class LabelCell extends TextFieldTreeTableCell { } } + @Override + public IntegerProperty getConfirmationsProperty() { + if(confirmationsProperty == null) { + confirmationsProperty = new SimpleIntegerProperty(); + confirmationsProperty.addListener((observable, oldValue, newValue) -> { + if(newValue.intValue() >= BlockTransactionHash.BLOCKS_TO_CONFIRM) { + getStyleClass().remove("confirming"); + confirmationsProperty.unbind(); + } + }); + } + + return confirmationsProperty; + } + private static class LabelContextMenu extends ContextMenu { public LabelContextMenu(Entry entry, String label) { MenuItem copyLabel = new MenuItem("Copy Label"); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java index e47a6337..b628fde9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java @@ -97,17 +97,6 @@ public class TransactionEntry extends Entry implements Comparable walletTxos) { int validEntries = 0; for(TransactionInput txInput : blockTransaction.getTransaction().getInputs()) {