improve encapsulation and binding lifecycle of cell confirmation listeners

This commit is contained in:
Craig Raw 2022-11-30 11:12:01 +02:00
parent 4b32eb397e
commit 6ac294920e
5 changed files with 128 additions and 31 deletions

View file

@ -2,11 +2,14 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
import com.sparrowwallet.sparrow.UnitFormat; import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.wallet.Entry; import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.HashIndexEntry; import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
import com.sparrowwallet.sparrow.wallet.TransactionEntry; import com.sparrowwallet.sparrow.wallet.TransactionEntry;
import com.sparrowwallet.sparrow.wallet.UtxoEntry; 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.ContentDisplay;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeTableCell; import javafx.scene.control.TreeTableCell;
@ -16,12 +19,14 @@ import org.controlsfx.tools.Platform;
import java.text.DecimalFormat; import java.text.DecimalFormat;
class CoinCell extends TreeTableCell<Entry, Number> { class CoinCell extends TreeTableCell<Entry, Number> implements ConfirmationsListener {
private final Tooltip tooltip; private final CoinTooltip tooltip;
private IntegerProperty confirmationsProperty;
public CoinCell() { public CoinCell() {
super(); super();
tooltip = new Tooltip(); tooltip = new CoinTooltip();
tooltip.setShowDelay(Duration.millis(500)); tooltip.setShowDelay(Duration.millis(500));
getStyleClass().add("coin-cell"); getStyleClass().add("coin-cell");
if(Platform.getCurrent() == Platform.OSX) { if(Platform.getCurrent() == Platform.OSX) {
@ -50,21 +55,16 @@ class CoinCell extends TreeTableCell<Entry, Number> {
final String btcValue = decimalFormat.format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN); final String btcValue = decimalFormat.format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN);
if(unit.equals(BitcoinUnit.BTC)) { if(unit.equals(BitcoinUnit.BTC)) {
tooltip.setText(satsValue + " " + BitcoinUnit.SATOSHIS.getLabel()); tooltip.setValue(satsValue + " " + BitcoinUnit.SATOSHIS.getLabel());
setText(btcValue); setText(btcValue);
} else { } else {
tooltip.setText(btcValue + " " + BitcoinUnit.BTC.getLabel()); tooltip.setValue(btcValue + " " + BitcoinUnit.BTC.getLabel());
setText(satsValue); setText(satsValue);
} }
setTooltip(tooltip); setTooltip(tooltip);
String tooltipValue = tooltip.getText();
if(entry instanceof TransactionEntry transactionEntry) { if(entry instanceof TransactionEntry transactionEntry) {
tooltip.setText(tooltipValue + " (" + transactionEntry.getConfirmationsDescription() + ")"); tooltip.showConfirmations(transactionEntry.confirmationsProperty());
transactionEntry.confirmationsProperty().addListener((observable, oldValue, newValue) -> {
tooltip.setText(tooltipValue + " (" + transactionEntry.getConfirmationsDescription() + ")");
});
if(transactionEntry.isConfirming()) { if(transactionEntry.isConfirming()) {
ConfirmationProgressIndicator arc = new ConfirmationProgressIndicator(transactionEntry.getConfirmations()); ConfirmationProgressIndicator arc = new ConfirmationProgressIndicator(transactionEntry.getConfirmations());
@ -94,4 +94,65 @@ class CoinCell extends TreeTableCell<Entry, Number> {
} }
} }
} }
@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";
}
}
}
} }

View file

@ -0,0 +1,7 @@
package com.sparrowwallet.sparrow.control;
import javafx.beans.property.IntegerProperty;
public interface ConfirmationsListener {
IntegerProperty getConfirmationsProperty();
}

View file

@ -12,6 +12,8 @@ import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.*; import com.sparrowwallet.sparrow.wallet.*;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.input.Clipboard; import javafx.scene.input.Clipboard;
@ -30,7 +32,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class EntryCell extends TreeTableCell<Entry, Entry> { public class EntryCell extends TreeTableCell<Entry, Entry> implements ConfirmationsListener {
private static final Logger log = LoggerFactory.getLogger(EntryCell.class); private static final Logger log = LoggerFactory.getLogger(EntryCell.class);
public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm"); public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
@ -38,6 +40,8 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
private static EntryCell lastCell; private static EntryCell lastCell;
private IntegerProperty confirmationsProperty;
public EntryCell() { public EntryCell() {
super(); super();
setAlignment(Pos.CENTER_LEFT); setAlignment(Pos.CENTER_LEFT);
@ -189,6 +193,21 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
} }
} }
@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) { private static void increaseFee(TransactionEntry transactionEntry, boolean cancelTransaction) {
BlockTransaction blockTransaction = transactionEntry.getBlockTransaction(); BlockTransaction blockTransaction = transactionEntry.getBlockTransaction();
Map<BlockTransactionHashIndex, WalletNode> walletTxos = transactionEntry.getWallet().getWalletTxos(); Map<BlockTransactionHashIndex, WalletNode> walletTxos = transactionEntry.getWallet().getWalletTxos();
@ -722,13 +741,14 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
if(entry instanceof TransactionEntry) { if(entry instanceof TransactionEntry) {
cell.getStyleClass().add("transaction-row"); cell.getStyleClass().add("transaction-row");
TransactionEntry transactionEntry = (TransactionEntry)entry; TransactionEntry transactionEntry = (TransactionEntry)entry;
if(cell instanceof ConfirmationsListener confirmationsListener) {
if(transactionEntry.isConfirming()) { if(transactionEntry.isConfirming()) {
cell.getStyleClass().add("confirming"); cell.getStyleClass().add("confirming");
transactionEntry.confirmationsProperty().addListener((observable, oldValue, newValue) -> { confirmationsListener.getConfirmationsProperty().bind(transactionEntry.confirmationsProperty());
if(!transactionEntry.isConfirming()) { } else {
cell.getStyleClass().remove("confirming"); confirmationsListener.getConfirmationsProperty().unbind();
} }
});
} }
} else if(entry instanceof NodeEntry) { } else if(entry instanceof NodeEntry) {
cell.getStyleClass().add("node-row"); cell.getStyleClass().add("node-row");

View file

@ -1,6 +1,9 @@
package com.sparrowwallet.sparrow.control; package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
import com.sparrowwallet.sparrow.wallet.Entry; import com.sparrowwallet.sparrow.wallet.Entry;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.Event; import javafx.event.Event;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTreeTableCell; import javafx.scene.control.cell.TextFieldTreeTableCell;
@ -13,9 +16,11 @@ import org.slf4j.LoggerFactory;
import java.lang.reflect.Field; import java.lang.reflect.Field;
class LabelCell extends TextFieldTreeTableCell<Entry, String> { class LabelCell extends TextFieldTreeTableCell<Entry, String> implements ConfirmationsListener {
private static final Logger log = LoggerFactory.getLogger(LabelCell.class); private static final Logger log = LoggerFactory.getLogger(LabelCell.class);
private IntegerProperty confirmationsProperty;
public LabelCell() { public LabelCell() {
super(new DefaultStringConverter()); super(new DefaultStringConverter());
getStyleClass().add("label-cell"); getStyleClass().add("label-cell");
@ -81,6 +86,21 @@ class LabelCell extends TextFieldTreeTableCell<Entry, String> {
} }
} }
@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 { private static class LabelContextMenu extends ContextMenu {
public LabelContextMenu(Entry entry, String label) { public LabelContextMenu(Entry entry, String label) {
MenuItem copyLabel = new MenuItem("Copy Label"); MenuItem copyLabel = new MenuItem("Copy Label");

View file

@ -97,17 +97,6 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
return blockTransaction.getConfirmations(AppServices.getCurrentBlockHeight() == null ? getWallet().getStoredBlockHeight() : AppServices.getCurrentBlockHeight()); return blockTransaction.getConfirmations(AppServices.getCurrentBlockHeight() == null ? getWallet().getStoredBlockHeight() : AppServices.getCurrentBlockHeight());
} }
public String getConfirmationsDescription() {
int confirmations = getConfirmations();
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";
}
}
public boolean isComplete(Map<HashIndex, BlockTransactionHashIndex> walletTxos) { public boolean isComplete(Map<HashIndex, BlockTransactionHashIndex> walletTxos) {
int validEntries = 0; int validEntries = 0;
for(TransactionInput txInput : blockTransaction.getTransaction().getInputs()) { for(TransactionInput txInput : blockTransaction.getTransaction().getInputs()) {