pay to the next fresh address of any open wallet via dropdown on send tab address field

This commit is contained in:
Craig Raw 2021-10-29 13:23:27 +02:00
parent 180e76f0f8
commit 6b6b23b51a
11 changed files with 119 additions and 16 deletions

2
drongo

@ -1 +1 @@
Subproject commit 360550a7183683ad4ee929a4c5411075bd751ffd Subproject commit bdb9900d8d94f62b8714e14e151635813c7c258d

View file

@ -912,7 +912,7 @@ public class AppController implements Initializable {
if(!wallet.isMasterWallet() && wallet.getMasterWallet().getKeystores().size() == copy.getKeystores().size() && wallet.getMasterWallet().getKeystores().get(i).hasSeed()) { if(!wallet.isMasterWallet() && wallet.getMasterWallet().getKeystores().size() == copy.getKeystores().size() && wallet.getMasterWallet().getKeystores().get(i).hasSeed()) {
copyKeystore.getSeed().setPassphrase(wallet.getMasterWallet().getKeystores().get(i).getSeed().getPassphrase()); copyKeystore.getSeed().setPassphrase(wallet.getMasterWallet().getKeystores().get(i).getSeed().getPassphrase());
} else { } else {
KeystorePassphraseDialog passphraseDialog = new KeystorePassphraseDialog(wallet.getFullName(), copyKeystore); KeystorePassphraseDialog passphraseDialog = new KeystorePassphraseDialog(wallet.getFullDisplayName(), copyKeystore);
Optional<String> optionalPassphrase = passphraseDialog.showAndWait(); Optional<String> optionalPassphrase = passphraseDialog.showAndWait();
if(optionalPassphrase.isPresent()) { if(optionalPassphrase.isPresent()) {
copyKeystore.getSeed().setPassphrase(optionalPassphrase.get()); copyKeystore.getSeed().setPassphrase(optionalPassphrase.get());

View file

@ -0,0 +1,47 @@
package com.sparrowwallet.sparrow.control;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import org.controlsfx.control.textfield.CustomTextField;
public class ComboBoxTextField extends CustomTextField {
private final ObjectProperty<ComboBox<?>> comboProperty = new SimpleObjectProperty<>();
public ComboBoxTextField() {
super();
getStyleClass().add("combo-text-field");
setupCopyButtonField(super.rightProperty());
}
private void setupCopyButtonField(ObjectProperty<Node> rightProperty) {
Region showComboButton = new Region();
showComboButton.getStyleClass().addAll("graphic"); //$NON-NLS-1$
StackPane showComboButtonPane = new StackPane(showComboButton);
showComboButtonPane.getStyleClass().addAll("combo-button"); //$NON-NLS-1$
showComboButtonPane.setCursor(Cursor.DEFAULT);
showComboButtonPane.setOnMouseReleased(e -> {
if(comboProperty.isNotNull().get()) {
comboProperty.get().show();
}
});
rightProperty.set(showComboButtonPane);
}
public ComboBox<?> getComboProperty() {
return comboProperty.get();
}
public ObjectProperty<ComboBox<?>> walletComboProperty() {
return comboProperty;
}
public void setComboProperty(ComboBox<?> comboProperty) {
this.comboProperty.set(comboProperty);
}
}

View file

@ -28,7 +28,7 @@ public class KeystorePassphraseDialog extends Dialog<String> {
this.passphrase = (CustomPasswordField) TextFields.createClearablePasswordField(); this.passphrase = (CustomPasswordField) TextFields.createClearablePasswordField();
final DialogPane dialogPane = getDialogPane(); final DialogPane dialogPane = getDialogPane();
setTitle("Keystore Passphrase" + (walletName != null ? " - " + walletName : "")); setTitle("Keystore Passphrase" + (walletName != null ? " for " + walletName : ""));
dialogPane.setHeaderText((confirm ? "Re-enter" : "Enter") + " the BIP39 passphrase\n" + (confirm ? "to confirm:" : "for keystore: " + keystore.getLabel())); dialogPane.setHeaderText((confirm ? "Re-enter" : "Enter") + " the BIP39 passphrase\n" + (confirm ? "to confirm:" : "for keystore: " + keystore.getLabel()));
dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm()); dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm());
AppServices.setStageIcon(dialogPane.getScene().getWindow()); AppServices.setStageIcon(dialogPane.getScene().getWindow());

View file

@ -222,7 +222,7 @@ public class TransactionDiagram extends GridPane {
Tooltip tooltip = new Tooltip(); Tooltip tooltip = new Tooltip();
if(walletNode != null) { if(walletNode != null) {
tooltip.setText("Spending " + getSatsValue(input.getValue()) + " sats from " + (isFinal() ? walletTx.getWallet().getFullName() : "") + " " + walletNode + "\n" + input.getHashAsString() + ":" + input.getIndex() + "\n" + walletTx.getWallet().getAddress(walletNode)); tooltip.setText("Spending " + getSatsValue(input.getValue()) + " sats from " + (isFinal() ? walletTx.getWallet().getFullDisplayName() : "") + " " + walletNode + "\n" + input.getHashAsString() + ":" + input.getIndex() + "\n" + walletTx.getWallet().getAddress(walletNode));
tooltip.getStyleClass().add("input-label"); tooltip.getStyleClass().add("input-label");
if(input.getLabel() == null || input.getLabel().isEmpty()) { if(input.getLabel() == null || input.getLabel().isEmpty()) {
@ -400,7 +400,7 @@ public class TransactionDiagram extends GridPane {
WalletNode toNode = walletTx.getWallet() != null ? walletTx.getWallet().getWalletAddresses().get(payment.getAddress()) : null; WalletNode toNode = walletTx.getWallet() != null ? walletTx.getWallet().getWalletAddresses().get(payment.getAddress()) : null;
Tooltip recipientTooltip = new Tooltip((toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ") Tooltip recipientTooltip = new Tooltip((toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ")
+ getSatsValue(payment.getAmount()) + " sats to " + getSatsValue(payment.getAmount()) + " sats to "
+ (payment instanceof AdditionalPayment ? "\n" + payment : (toWallet == null ? (payment.getLabel() == null ? (toNode != null ? toNode : "external address") : payment.getLabel()) : toWallet.getFullName()) + "\n" + payment.getAddress().toString())); + (payment instanceof AdditionalPayment ? "\n" + payment : (toWallet == null ? (payment.getLabel() == null ? (toNode != null ? toNode : "external address") : payment.getLabel()) : toWallet.getFullDisplayName()) + "\n" + payment.getAddress().toString()));
recipientTooltip.getStyleClass().add("recipient-label"); recipientTooltip.getStyleClass().add("recipient-label");
recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY)); recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
recipientLabel.setTooltip(recipientTooltip); recipientLabel.setTooltip(recipientTooltip);

View file

@ -146,7 +146,7 @@ public class InputController extends TransactionFormController implements Initia
String baseText = getLegendText(txInput); String baseText = getLegendText(txInput);
if(signingWallet != null) { if(signingWallet != null) {
if(inputForm.isWalletTxo()) { if(inputForm.isWalletTxo()) {
inputFieldset.setText(baseText + " - " + signingWallet.getFullName()); inputFieldset.setText(baseText + " from " + signingWallet.getFullDisplayName());
inputFieldset.setIcon(TransactionDiagram.getTxoGlyph()); inputFieldset.setIcon(TransactionDiagram.getTxoGlyph());
} else { } else {
inputFieldset.setText(baseText + " - Payjoin"); inputFieldset.setText(baseText + " - Payjoin");

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.wallet;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.address.InvalidAddressException; import com.sparrowwallet.drongo.address.InvalidAddressException;
import com.sparrowwallet.drongo.address.P2PKHAddress; import com.sparrowwallet.drongo.address.P2PKHAddress;
@ -16,6 +17,7 @@ import com.sparrowwallet.sparrow.control.*;
import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent; import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent;
import com.sparrowwallet.sparrow.event.ExchangeRatesUpdatedEvent; import com.sparrowwallet.sparrow.event.ExchangeRatesUpdatedEvent;
import com.sparrowwallet.sparrow.event.FiatCurrencySelectedEvent; import com.sparrowwallet.sparrow.event.FiatCurrencySelectedEvent;
import com.sparrowwallet.sparrow.event.OpenWalletsEvent;
import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.net.ExchangeSource; import com.sparrowwallet.sparrow.net.ExchangeSource;
import javafx.application.Platform; import javafx.application.Platform;
@ -23,11 +25,13 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.util.StringConverter;
import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport; import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator; import org.controlsfx.validation.Validator;
@ -38,6 +42,7 @@ import java.net.URL;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog; import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
@ -49,7 +54,10 @@ public class PaymentController extends WalletFormController implements Initializ
private ValidationSupport validationSupport; private ValidationSupport validationSupport;
@FXML @FXML
private CopyableTextField address; private ComboBox<Wallet> openWallets;
@FXML
private ComboBoxTextField address;
@FXML @FXML
private TextField label; private TextField label;
@ -121,6 +129,27 @@ public class PaymentController extends WalletFormController implements Initializ
@Override @Override
public void initializeView() { public void initializeView() {
openWallets.setConverter(new StringConverter<>() {
@Override
public String toString(Wallet wallet) {
return wallet == null ? "" : wallet.getFullDisplayName() + (wallet == sendController.getWalletForm().getWallet() ? " (Consolidation)" : "");
}
@Override
public Wallet fromString(String string) {
return null;
}
});
openWallets.setItems(FXCollections.observableList(AppServices.get().getOpenWallets().keySet().stream().filter(Wallet::isValid).collect(Collectors.toList())));
openWallets.prefWidthProperty().bind(address.widthProperty());
openWallets.valueProperty().addListener((observable, oldValue, newValue) -> {
if(newValue != null) {
WalletNode freshNode = newValue.getFreshNode(KeyPurpose.RECEIVE);
Address freshAddress = newValue.getAddress(freshNode);
address.setText(freshAddress.toString());
}
});
address.textProperty().addListener((observable, oldValue, newValue) -> { address.textProperty().addListener((observable, oldValue, newValue) -> {
try { try {
BitcoinURI bitcoinURI = new BitcoinURI(newValue); BitcoinURI bitcoinURI = new BitcoinURI(newValue);
@ -443,4 +472,9 @@ public class PaymentController extends WalletFormController implements Initializ
public void exchangeRatesUpdated(ExchangeRatesUpdatedEvent event) { public void exchangeRatesUpdated(ExchangeRatesUpdatedEvent event) {
setFiatAmount(event.getCurrencyRate(), getRecipientValueSats()); setFiatAmount(event.getCurrencyRate(), getRecipientValueSats());
} }
@Subscribe
public void openWallets(OpenWalletsEvent event) {
openWallets.setItems(FXCollections.observableList(event.getWallets().stream().filter(Wallet::isValid).collect(Collectors.toList())));
}
} }

View file

@ -439,7 +439,7 @@ public class SettingsController extends WalletFormController implements Initiali
} }
if(wallet == null) { if(wallet == null) {
throw new IllegalStateException("Cannot find child wallet " + walletForm.getWallet().getFullName() + " to export"); throw new IllegalStateException("Cannot find child wallet " + walletForm.getWallet().getFullDisplayName() + " to export");
} }
WalletExportDialog dlg = new WalletExportDialog(wallet); WalletExportDialog dlg = new WalletExportDialog(wallet);

View file

@ -102,7 +102,7 @@ public class TransactionsController extends WalletFormController implements Init
}); });
transactionsMasterDetail.setShowDetailNode(Config.get().isShowLoadingLog()); transactionsMasterDetail.setShowDetailNode(Config.get().isShowLoadingLog());
loadingLog.appendText("Wallet loading history for " + getWalletForm().getWallet().getFullName()); loadingLog.appendText("Wallet loading history for " + getWalletForm().getWallet().getFullDisplayName());
loadingLog.setEditable(false); loadingLog.setEditable(false);
} }

View file

@ -132,6 +132,25 @@
-fx-background-color: #116a8d; -fx-background-color: #116a8d;
} }
.combo-text-field .combo-button {
-fx-padding: 0 3 0 0;
}
.combo-text-field .combo-button > .graphic {
-fx-background-color: #949494;
-fx-scale-shape: false;
-fx-padding: 4.5 6 4.5 4.5;
-fx-shape: "M11.8513+192L19.004+192C19.4989+192+19.7463+192.554+19.396+192.878L15.821+196.191C15.6042+196.392+15.2511+196.392+15.0343+196.191L11.4594+192.878C11.1091+192.554+11.3565+192+11.8513+192Z ";
}
.combo-text-field .combo-button:hover > .graphic {
-fx-background-color: #0184bc;
}
.combo-text-field .combo-button:pressed > .graphic {
-fx-background-color: #116a8d;
}
.readonly.text-input { .readonly.text-input {
-fx-text-fill: derive(-fx-text-inner-color, 40%); -fx-text-fill: derive(-fx-text-inner-color, 40%);
} }

View file

@ -9,13 +9,13 @@
<?import tornadofx.control.Form?> <?import tornadofx.control.Form?>
<?import tornadofx.control.Fieldset?> <?import tornadofx.control.Fieldset?>
<?import tornadofx.control.Field?> <?import tornadofx.control.Field?>
<?import com.sparrowwallet.sparrow.control.CopyableTextField?>
<?import javafx.collections.FXCollections?> <?import javafx.collections.FXCollections?>
<?import com.sparrowwallet.sparrow.control.FiatLabel?> <?import com.sparrowwallet.sparrow.control.FiatLabel?>
<?import com.sparrowwallet.drongo.BitcoinUnit?> <?import com.sparrowwallet.drongo.BitcoinUnit?>
<?import org.controlsfx.glyphfont.Glyph?> <?import org.controlsfx.glyphfont.Glyph?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import com.sparrowwallet.sparrow.control.ComboBoxTextField?>
<GridPane styleClass="send-form" hgap="10.0" vgap="10.0" stylesheets="@payment.css, @send.css, @wallet.css, @../script.css, @../general.css" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.PaymentController"> <GridPane styleClass="send-form" hgap="10.0" vgap="10.0" stylesheets="@payment.css, @send.css, @wallet.css, @../script.css, @../general.css" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.PaymentController">
<padding> <padding>
<Insets top="10.0" bottom="10.0" /> <Insets top="10.0" bottom="10.0" />
@ -31,11 +31,14 @@
<Form GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"> <Form GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2">
<Fieldset inputGrow="ALWAYS"> <Fieldset inputGrow="ALWAYS">
<Field text="Pay to:"> <Field text="Pay to:">
<CopyableTextField fx:id="address" styleClass="address-text-field"> <StackPane>
<ComboBox fx:id="openWallets" />
<ComboBoxTextField fx:id="address" styleClass="address-text-field" comboProperty="$openWallets">
<tooltip> <tooltip>
<Tooltip text="Address or bitcoin: URI"/> <Tooltip text="Address or bitcoin: URI"/>
</tooltip> </tooltip>
</CopyableTextField> </ComboBoxTextField>
</StackPane>
</Field> </Field>
<Field text="Label:"> <Field text="Label:">
<TextField fx:id="label"> <TextField fx:id="label">