mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46: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.WalletNode;
|
||||
import com.sparrowwallet.drongo.wallet.WalletTransaction;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.ExcludeUtxoEvent;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.*;
|
||||
|
@ -146,6 +150,16 @@ public class TransactionDiagram extends GridPane {
|
|||
WalletNode walletNode = displayedUtxos.get(input);
|
||||
String desc = getInputDescription(input);
|
||||
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();
|
||||
if(walletNode != null) {
|
||||
|
@ -324,6 +338,13 @@ public class TransactionDiagram extends GridPane {
|
|||
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() {
|
||||
Glyph paymentGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.SEND);
|
||||
paymentGlyph.getStyleClass().add("payment-icon");
|
||||
|
@ -332,35 +353,35 @@ public class TransactionDiagram extends GridPane {
|
|||
}
|
||||
|
||||
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.setFontSize(12);
|
||||
return consolidationGlyph;
|
||||
}
|
||||
|
||||
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.setFontSize(12);
|
||||
return changeGlyph;
|
||||
}
|
||||
|
||||
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.setFontSize(12);
|
||||
return feeGlyph;
|
||||
}
|
||||
|
||||
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.setFontSize(12);
|
||||
return feeWarningGlyph;
|
||||
}
|
||||
|
||||
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.setFontSize(12);
|
||||
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'),
|
||||
SD_CARD('\uf7c2'),
|
||||
SEARCH('\uf002'),
|
||||
TIMES_CIRCLE('\uf057'),
|
||||
TOOLS('\uf7d9'),
|
||||
UNDO('\uf0e2'),
|
||||
WALLET('\uf555');
|
||||
|
|
|
@ -93,6 +93,8 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
|
||||
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 BooleanProperty insufficientInputsProperty = new SimpleBooleanProperty(false);
|
||||
|
@ -256,16 +258,11 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
});
|
||||
|
||||
utxoSelectorProperty.addListener((observable, oldValue, utxoSelector) -> {
|
||||
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 {
|
||||
maxButton.setText("Max");
|
||||
clearButton.setText("Clear");
|
||||
}
|
||||
updateMaxClearButtons(utxoSelector, utxoFilterProperty.get());
|
||||
});
|
||||
|
||||
utxoFilterProperty.addListener((observable, oldValue, utxoFilter) -> {
|
||||
updateMaxClearButtons(utxoSelectorProperty.get(), utxoFilter);
|
||||
});
|
||||
|
||||
walletTransactionProperty.addListener((observable, oldValue, walletTransaction) -> {
|
||||
|
@ -328,7 +325,7 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
Integer currentBlockHeight = AppController.getCurrentBlockHeight();
|
||||
boolean groupByAddress = Config.get().isGroupByAddress();
|
||||
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);
|
||||
insufficientInputsProperty.set(false);
|
||||
|
||||
|
@ -355,6 +352,15 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
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() {
|
||||
try {
|
||||
getRecipientAddress();
|
||||
|
@ -471,10 +477,6 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
return targetBlocks.lookup(".thumb");
|
||||
}
|
||||
|
||||
public void setUtxoSelector(UtxoSelector utxoSelector) {
|
||||
utxoSelectorProperty.set(utxoSelector);
|
||||
}
|
||||
|
||||
public void setMaxInput(ActionEvent event) {
|
||||
UtxoSelector utxoSelector = utxoSelectorProperty.get();
|
||||
if(utxoSelector == null) {
|
||||
|
@ -509,6 +511,25 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
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) {
|
||||
QRScanDialog qrScanDialog = new QRScanDialog();
|
||||
Optional<QRScanDialog.Result> optionalResult = qrScanDialog.showAndWait();
|
||||
|
@ -547,6 +568,7 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
userFeeSet.set(false);
|
||||
targetBlocks.setValue(4);
|
||||
utxoSelectorProperty.setValue(null);
|
||||
utxoFilterProperty.setValue(null);
|
||||
walletTransactionProperty.setValue(null);
|
||||
|
||||
validationSupport.setErrorDecorationEnabled(false);
|
||||
|
@ -621,7 +643,8 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
public void spendUtxos(SpendUtxoEvent event) {
|
||||
if(!event.getUtxoEntries().isEmpty() && event.getUtxoEntries().get(0).getWallet().equals(getWalletForm().getWallet())) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -649,4 +672,37 @@ public class SendController extends WalletFormController implements Initializabl
|
|||
setFiatAmount(event.getCurrencyRate(), getRecipientValueSats());
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,4 +65,20 @@
|
|||
-fx-text-fill: #696c77;
|
||||
-fx-stroke: #696c77;
|
||||
-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