mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-04 21:36:45 +00:00
exclude utxos from transaction diagram
This commit is contained in:
parent
32bc9f6a99
commit
eb09076604
6 changed files with 138 additions and 22 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit b2297a8d0219af70b3e97c83feb1af8aec485d9f
|
Subproject commit e8d8fa61268ec8ac4dd5c14e6715d4a4bde2fe49
|
|
@ -4,11 +4,15 @@ import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletTransaction;
|
import com.sparrowwallet.drongo.wallet.WalletTransaction;
|
||||||
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
import com.sparrowwallet.sparrow.event.ExcludeUtxoEvent;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Tooltip;
|
import javafx.scene.control.Tooltip;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
|
@ -146,6 +150,16 @@ public class TransactionDiagram extends GridPane {
|
||||||
WalletNode walletNode = displayedUtxos.get(input);
|
WalletNode walletNode = displayedUtxos.get(input);
|
||||||
String desc = getInputDescription(input);
|
String desc = getInputDescription(input);
|
||||||
Label label = new Label(desc);
|
Label label = new Label(desc);
|
||||||
|
label.getStyleClass().add("utxo-label");
|
||||||
|
|
||||||
|
Button excludeUtxoButton = new Button("");
|
||||||
|
excludeUtxoButton.setGraphic(getExcludeGlyph());
|
||||||
|
excludeUtxoButton.setOnAction(event -> {
|
||||||
|
EventManager.get().post(new ExcludeUtxoEvent(walletTx, input));
|
||||||
|
});
|
||||||
|
|
||||||
|
label.setGraphic(excludeUtxoButton);
|
||||||
|
label.setContentDisplay(ContentDisplay.LEFT);
|
||||||
|
|
||||||
Tooltip tooltip = new Tooltip();
|
Tooltip tooltip = new Tooltip();
|
||||||
if(walletNode != null) {
|
if(walletNode != null) {
|
||||||
|
@ -324,6 +338,13 @@ public class TransactionDiagram extends GridPane {
|
||||||
return spacer;
|
return spacer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
public static Glyph getPaymentGlyph() {
|
||||||
Glyph paymentGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.SEND);
|
Glyph paymentGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.SEND);
|
||||||
paymentGlyph.getStyleClass().add("payment-icon");
|
paymentGlyph.getStyleClass().add("payment-icon");
|
||||||
|
@ -332,35 +353,35 @@ public class TransactionDiagram extends GridPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Glyph getConsolidationGlyph() {
|
public static Glyph getConsolidationGlyph() {
|
||||||
Glyph consolidationGlyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.REPLY_ALL);
|
Glyph consolidationGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.REPLY_ALL);
|
||||||
consolidationGlyph.getStyleClass().add("consolidation-icon");
|
consolidationGlyph.getStyleClass().add("consolidation-icon");
|
||||||
consolidationGlyph.setFontSize(12);
|
consolidationGlyph.setFontSize(12);
|
||||||
return consolidationGlyph;
|
return consolidationGlyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Glyph getChangeGlyph() {
|
public static Glyph getChangeGlyph() {
|
||||||
Glyph changeGlyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.COINS);
|
Glyph changeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.COINS);
|
||||||
changeGlyph.getStyleClass().add("change-icon");
|
changeGlyph.getStyleClass().add("change-icon");
|
||||||
changeGlyph.setFontSize(12);
|
changeGlyph.setFontSize(12);
|
||||||
return changeGlyph;
|
return changeGlyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Glyph getFeeGlyph() {
|
private Glyph getFeeGlyph() {
|
||||||
Glyph feeGlyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.HAND_HOLDING);
|
Glyph feeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING);
|
||||||
feeGlyph.getStyleClass().add("fee-icon");
|
feeGlyph.getStyleClass().add("fee-icon");
|
||||||
feeGlyph.setFontSize(12);
|
feeGlyph.setFontSize(12);
|
||||||
return feeGlyph;
|
return feeGlyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Glyph getWarningGlyph() {
|
private Glyph getWarningGlyph() {
|
||||||
Glyph feeWarningGlyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.EXCLAMATION_CIRCLE);
|
Glyph feeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_CIRCLE);
|
||||||
feeWarningGlyph.getStyleClass().add("fee-warning-icon");
|
feeWarningGlyph.getStyleClass().add("fee-warning-icon");
|
||||||
feeWarningGlyph.setFontSize(12);
|
feeWarningGlyph.setFontSize(12);
|
||||||
return feeWarningGlyph;
|
return feeWarningGlyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Glyph getLockGlyph() {
|
private Glyph getLockGlyph() {
|
||||||
Glyph lockGlyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.LOCK);
|
Glyph lockGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.LOCK);
|
||||||
lockGlyph.getStyleClass().add("lock-icon");
|
lockGlyph.getStyleClass().add("lock-icon");
|
||||||
lockGlyph.setFontSize(12);
|
lockGlyph.setFontSize(12);
|
||||||
return lockGlyph;
|
return lockGlyph;
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||||
|
import com.sparrowwallet.drongo.wallet.WalletTransaction;
|
||||||
|
|
||||||
|
public class ExcludeUtxoEvent {
|
||||||
|
private final WalletTransaction walletTransaction;
|
||||||
|
private final BlockTransactionHashIndex utxo;
|
||||||
|
|
||||||
|
public ExcludeUtxoEvent(WalletTransaction walletTransaction, BlockTransactionHashIndex utxo) {
|
||||||
|
this.walletTransaction = walletTransaction;
|
||||||
|
this.utxo = utxo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WalletTransaction getWalletTransaction() {
|
||||||
|
return walletTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockTransactionHashIndex getUtxo() {
|
||||||
|
return utxo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ public class FontAwesome5 extends GlyphFont {
|
||||||
SATELLITE_DISH('\uf7c0'),
|
SATELLITE_DISH('\uf7c0'),
|
||||||
SD_CARD('\uf7c2'),
|
SD_CARD('\uf7c2'),
|
||||||
SEARCH('\uf002'),
|
SEARCH('\uf002'),
|
||||||
|
TIMES_CIRCLE('\uf057'),
|
||||||
TOOLS('\uf7d9'),
|
TOOLS('\uf7d9'),
|
||||||
UNDO('\uf0e2'),
|
UNDO('\uf0e2'),
|
||||||
WALLET('\uf555');
|
WALLET('\uf555');
|
||||||
|
|
|
@ -93,6 +93,8 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
|
|
||||||
private final ObjectProperty<UtxoSelector> utxoSelectorProperty = new SimpleObjectProperty<>(null);
|
private final ObjectProperty<UtxoSelector> utxoSelectorProperty = new SimpleObjectProperty<>(null);
|
||||||
|
|
||||||
|
private final ObjectProperty<UtxoFilter> utxoFilterProperty = new SimpleObjectProperty<>(null);
|
||||||
|
|
||||||
private final ObjectProperty<WalletTransaction> walletTransactionProperty = new SimpleObjectProperty<>(null);
|
private final ObjectProperty<WalletTransaction> walletTransactionProperty = new SimpleObjectProperty<>(null);
|
||||||
|
|
||||||
private final BooleanProperty insufficientInputsProperty = new SimpleBooleanProperty(false);
|
private final BooleanProperty insufficientInputsProperty = new SimpleBooleanProperty(false);
|
||||||
|
@ -256,16 +258,11 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
});
|
});
|
||||||
|
|
||||||
utxoSelectorProperty.addListener((observable, oldValue, utxoSelector) -> {
|
utxoSelectorProperty.addListener((observable, oldValue, utxoSelector) -> {
|
||||||
if(utxoSelector instanceof PresetUtxoSelector) {
|
updateMaxClearButtons(utxoSelector, utxoFilterProperty.get());
|
||||||
PresetUtxoSelector presetUtxoSelector = (PresetUtxoSelector)utxoSelector;
|
});
|
||||||
int num = presetUtxoSelector.getPresetUtxos().size();
|
|
||||||
String selection = " (" + num + " UTXO" + (num > 1 ? "s" : "") + " selected)";
|
utxoFilterProperty.addListener((observable, oldValue, utxoFilter) -> {
|
||||||
maxButton.setText("Max" + selection);
|
updateMaxClearButtons(utxoSelectorProperty.get(), utxoFilter);
|
||||||
clearButton.setText("Clear" + selection);
|
|
||||||
} else {
|
|
||||||
maxButton.setText("Max");
|
|
||||||
clearButton.setText("Clear");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
walletTransactionProperty.addListener((observable, oldValue, walletTransaction) -> {
|
walletTransactionProperty.addListener((observable, oldValue, walletTransaction) -> {
|
||||||
|
@ -328,7 +325,7 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
Integer currentBlockHeight = AppController.getCurrentBlockHeight();
|
Integer currentBlockHeight = AppController.getCurrentBlockHeight();
|
||||||
boolean groupByAddress = Config.get().isGroupByAddress();
|
boolean groupByAddress = Config.get().isGroupByAddress();
|
||||||
boolean includeMempoolChange = Config.get().isIncludeMempoolChange();
|
boolean includeMempoolChange = Config.get().isIncludeMempoolChange();
|
||||||
WalletTransaction walletTransaction = wallet.createWalletTransaction(getUtxoSelectors(), recipientAddress, recipientAmount, getFeeRate(), getMinimumFeeRate(), userFee, currentBlockHeight, sendAll, groupByAddress, includeMempoolChange);
|
WalletTransaction walletTransaction = wallet.createWalletTransaction(getUtxoSelectors(), getUtxoFilters(), recipientAddress, recipientAmount, getFeeRate(), getMinimumFeeRate(), userFee, currentBlockHeight, sendAll, groupByAddress, includeMempoolChange);
|
||||||
walletTransactionProperty.setValue(walletTransaction);
|
walletTransactionProperty.setValue(walletTransaction);
|
||||||
insufficientInputsProperty.set(false);
|
insufficientInputsProperty.set(false);
|
||||||
|
|
||||||
|
@ -355,6 +352,15 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
return List.of(new BnBUtxoSelector(noInputsFee, costOfChange), new KnapsackUtxoSelector(noInputsFee));
|
return List.of(new BnBUtxoSelector(noInputsFee, costOfChange), new KnapsackUtxoSelector(noInputsFee));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<UtxoFilter> getUtxoFilters() {
|
||||||
|
UtxoFilter utxoFilter = utxoFilterProperty.get();
|
||||||
|
if(utxoFilter != null) {
|
||||||
|
return List.of(utxoFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isValidRecipientAddress() {
|
private boolean isValidRecipientAddress() {
|
||||||
try {
|
try {
|
||||||
getRecipientAddress();
|
getRecipientAddress();
|
||||||
|
@ -471,10 +477,6 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
return targetBlocks.lookup(".thumb");
|
return targetBlocks.lookup(".thumb");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUtxoSelector(UtxoSelector utxoSelector) {
|
|
||||||
utxoSelectorProperty.set(utxoSelector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMaxInput(ActionEvent event) {
|
public void setMaxInput(ActionEvent event) {
|
||||||
UtxoSelector utxoSelector = utxoSelectorProperty.get();
|
UtxoSelector utxoSelector = utxoSelectorProperty.get();
|
||||||
if(utxoSelector == null) {
|
if(utxoSelector == null) {
|
||||||
|
@ -509,6 +511,25 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
return address.getScriptType().getDustThreshold(txOutput, getFeeRate());
|
return address.getScriptType().getDustThreshold(txOutput, getFeeRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateMaxClearButtons(UtxoSelector utxoSelector, UtxoFilter utxoFilter) {
|
||||||
|
if(utxoSelector instanceof PresetUtxoSelector) {
|
||||||
|
PresetUtxoSelector presetUtxoSelector = (PresetUtxoSelector)utxoSelector;
|
||||||
|
int num = presetUtxoSelector.getPresetUtxos().size();
|
||||||
|
String selection = " (" + num + " UTXO" + (num != 1 ? "s" : "") + " selected)";
|
||||||
|
maxButton.setText("Max" + selection);
|
||||||
|
clearButton.setText("Clear" + selection);
|
||||||
|
} else if(utxoFilter instanceof ExcludeUtxoFilter) {
|
||||||
|
ExcludeUtxoFilter excludeUtxoFilter = (ExcludeUtxoFilter)utxoFilter;
|
||||||
|
int num = excludeUtxoFilter.getExcludedUtxos().size();
|
||||||
|
String exclusion = " (" + num + " UTXO" + (num != 1 ? "s" : "") + " excluded)";
|
||||||
|
maxButton.setText("Max" + exclusion);
|
||||||
|
clearButton.setText("Clear" + exclusion);
|
||||||
|
} else {
|
||||||
|
maxButton.setText("Max");
|
||||||
|
clearButton.setText("Clear");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void scanQrAddress(ActionEvent event) {
|
public void scanQrAddress(ActionEvent event) {
|
||||||
QRScanDialog qrScanDialog = new QRScanDialog();
|
QRScanDialog qrScanDialog = new QRScanDialog();
|
||||||
Optional<QRScanDialog.Result> optionalResult = qrScanDialog.showAndWait();
|
Optional<QRScanDialog.Result> optionalResult = qrScanDialog.showAndWait();
|
||||||
|
@ -547,6 +568,7 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
userFeeSet.set(false);
|
userFeeSet.set(false);
|
||||||
targetBlocks.setValue(4);
|
targetBlocks.setValue(4);
|
||||||
utxoSelectorProperty.setValue(null);
|
utxoSelectorProperty.setValue(null);
|
||||||
|
utxoFilterProperty.setValue(null);
|
||||||
walletTransactionProperty.setValue(null);
|
walletTransactionProperty.setValue(null);
|
||||||
|
|
||||||
validationSupport.setErrorDecorationEnabled(false);
|
validationSupport.setErrorDecorationEnabled(false);
|
||||||
|
@ -621,7 +643,8 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
public void spendUtxos(SpendUtxoEvent event) {
|
public void spendUtxos(SpendUtxoEvent event) {
|
||||||
if(!event.getUtxoEntries().isEmpty() && event.getUtxoEntries().get(0).getWallet().equals(getWalletForm().getWallet())) {
|
if(!event.getUtxoEntries().isEmpty() && event.getUtxoEntries().get(0).getWallet().equals(getWalletForm().getWallet())) {
|
||||||
List<BlockTransactionHashIndex> utxos = event.getUtxoEntries().stream().map(HashIndexEntry::getHashIndex).collect(Collectors.toList());
|
List<BlockTransactionHashIndex> utxos = event.getUtxoEntries().stream().map(HashIndexEntry::getHashIndex).collect(Collectors.toList());
|
||||||
setUtxoSelector(new PresetUtxoSelector(utxos));
|
utxoSelectorProperty.set(new PresetUtxoSelector(utxos));
|
||||||
|
utxoFilterProperty.set(null);
|
||||||
updateTransaction(true);
|
updateTransaction(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -649,4 +672,37 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
setFiatAmount(event.getCurrencyRate(), getRecipientValueSats());
|
setFiatAmount(event.getCurrencyRate(), getRecipientValueSats());
|
||||||
setFiatFeeAmount(event.getCurrencyRate(), getFeeValueSats());
|
setFiatFeeAmount(event.getCurrencyRate(), getFeeValueSats());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void excludeUtxo(ExcludeUtxoEvent event) {
|
||||||
|
if(event.getWalletTransaction() == walletTransactionProperty.get()) {
|
||||||
|
UtxoSelector utxoSelector = utxoSelectorProperty.get();
|
||||||
|
if(utxoSelector instanceof MaxUtxoSelector) {
|
||||||
|
Collection<BlockTransactionHashIndex> utxos = walletForm.getWallet().getWalletUtxos().keySet();
|
||||||
|
utxos.remove(event.getUtxo());
|
||||||
|
if(utxoFilterProperty.get() instanceof ExcludeUtxoFilter) {
|
||||||
|
ExcludeUtxoFilter existingUtxoFilter = (ExcludeUtxoFilter)utxoFilterProperty.get();
|
||||||
|
utxos.removeAll(existingUtxoFilter.getExcludedUtxos());
|
||||||
|
}
|
||||||
|
PresetUtxoSelector presetUtxoSelector = new PresetUtxoSelector(utxos);
|
||||||
|
utxoSelectorProperty.set(presetUtxoSelector);
|
||||||
|
updateTransaction(true);
|
||||||
|
} else if(utxoSelector instanceof PresetUtxoSelector) {
|
||||||
|
PresetUtxoSelector presetUtxoSelector = new PresetUtxoSelector(((PresetUtxoSelector)utxoSelector).getPresetUtxos());
|
||||||
|
presetUtxoSelector.getPresetUtxos().remove(event.getUtxo());
|
||||||
|
utxoSelectorProperty.set(presetUtxoSelector);
|
||||||
|
updateTransaction(true);
|
||||||
|
} else {
|
||||||
|
ExcludeUtxoFilter utxoFilter = new ExcludeUtxoFilter();
|
||||||
|
if(utxoFilterProperty.get() instanceof ExcludeUtxoFilter) {
|
||||||
|
ExcludeUtxoFilter existingUtxoFilter = (ExcludeUtxoFilter)utxoFilterProperty.get();
|
||||||
|
utxoFilter.getExcludedUtxos().addAll(existingUtxoFilter.getExcludedUtxos());
|
||||||
|
}
|
||||||
|
|
||||||
|
utxoFilter.getExcludedUtxos().add(event.getUtxo());
|
||||||
|
utxoFilterProperty.set(utxoFilter);
|
||||||
|
updateTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,3 +66,19 @@
|
||||||
-fx-stroke: #696c77;
|
-fx-stroke: #696c77;
|
||||||
-fx-stroke-width: 1px;
|
-fx-stroke-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#transactionDiagram .utxo-label .button {
|
||||||
|
-fx-padding: 0;
|
||||||
|
-fx-pref-height: 18;
|
||||||
|
-fx-pref-width: 18;
|
||||||
|
-fx-border-width: 0;
|
||||||
|
-fx-background-color: -fx-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#transactionDiagram .utxo-label .button .label .text {
|
||||||
|
-fx-fill: -fx-background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#transactionDiagram .utxo-label:hover .button .label .text {
|
||||||
|
-fx-fill: -fx-text-base-color;
|
||||||
|
}
|
Loading…
Reference in a new issue