mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2026-08-10 08:37:05 +00:00
implement silent payments receive ui
This commit is contained in:
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
|
||||
public class QRCodeDialog extends DialogWindow {
|
||||
public QRCodeDialog(String data) throws WriterException {
|
||||
super(data);
|
||||
super(data.length() > 70 ? data.substring(0, 70) + "..." : data);
|
||||
|
||||
setHints(List.of(Hint.CENTERED));
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.google.common.eventbus.Subscribe;
|
||||
import com.googlecode.lanterna.gui2.*;
|
||||
import com.sparrowwallet.drongo.KeyDerivation;
|
||||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||
@@ -38,19 +39,28 @@ public class ReceiveDialog extends WalletDialog {
|
||||
|
||||
Panel mainPanel = new Panel(new GridLayout(2).setHorizontalSpacing(2).setVerticalSpacing(1).setTopMarginSize(1));
|
||||
|
||||
boolean isSilentPayments = walletForm.getWallet().getPolicyType() == PolicyType.SINGLE_SP;
|
||||
|
||||
mainPanel.addComponent(new Label("Address"));
|
||||
address = new Label("").addTo(mainPanel);
|
||||
|
||||
mainPanel.addComponent(new Label("Derivation"));
|
||||
derivation = new Label("").addTo(mainPanel);
|
||||
if(!isSilentPayments) {
|
||||
mainPanel.addComponent(new Label("Derivation"));
|
||||
derivation = new Label("").addTo(mainPanel);
|
||||
|
||||
mainPanel.addComponent(new Label("Last Used"));
|
||||
lastUsed = new Label("").addTo(mainPanel);
|
||||
mainPanel.addComponent(new Label("Last Used"));
|
||||
lastUsed = new Label("").addTo(mainPanel);
|
||||
} else {
|
||||
derivation = new Label("");
|
||||
lastUsed = new Label("");
|
||||
}
|
||||
|
||||
Panel buttonPanel = new Panel();
|
||||
buttonPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(1));
|
||||
buttonPanel.addComponent(new Button("Back", () -> onBack(Function.RECEIVE)));
|
||||
buttonPanel.addComponent(new Button("Get Fresh Address", this::refreshAddress).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false)));
|
||||
if(!isSilentPayments) {
|
||||
buttonPanel.addComponent(new Button("Get Fresh Address", this::refreshAddress).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false)));
|
||||
}
|
||||
|
||||
mainPanel.addComponent(new Button("Show QR", this::showQR));
|
||||
|
||||
@@ -61,12 +71,17 @@ public class ReceiveDialog extends WalletDialog {
|
||||
}
|
||||
|
||||
public void showQR() {
|
||||
if(currentEntry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
QRCodeDialog qrCodeDialog = new QRCodeDialog(currentEntry.getAddress().toString());
|
||||
String qrAddress;
|
||||
if(getWalletForm().getWallet().getPolicyType() == PolicyType.SINGLE_SP) {
|
||||
qrAddress = getWalletForm().getWallet().getKeystores().getFirst().getSilentPaymentScanAddress().getAddress();
|
||||
} else if(currentEntry != null) {
|
||||
qrAddress = currentEntry.getAddress().toString();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
QRCodeDialog qrCodeDialog = new QRCodeDialog(qrAddress);
|
||||
qrCodeDialog.showDialog(SparrowTerminal.get().getGui());
|
||||
} catch(Exception e) {
|
||||
log.error("Error creating QR", e);
|
||||
@@ -76,6 +91,12 @@ public class ReceiveDialog extends WalletDialog {
|
||||
|
||||
public void refreshAddress() {
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||
if(getWalletForm().getWallet().getPolicyType() == PolicyType.SINGLE_SP) {
|
||||
String silentPaymentAddress = getWalletForm().getWallet().getKeystores().getFirst().getSilentPaymentScanAddress().getAddress();
|
||||
address.setText(silentPaymentAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
NodeEntry freshEntry = getWalletForm().getFreshNodeEntry(KeyPurpose.RECEIVE, currentEntry);
|
||||
setNodeEntry(freshEntry);
|
||||
});
|
||||
@@ -109,6 +130,10 @@ public class ReceiveDialog extends WalletDialog {
|
||||
}
|
||||
|
||||
private void updateLastUsed() {
|
||||
if(currentEntry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SparrowTerminal.get().getGuiThread().invokeLater(() -> {
|
||||
Set<BlockTransactionHashIndex> currentOutputs = currentEntry.getNode().getTransactionOutputs();
|
||||
if(AppServices.onlineProperty().get() && currentOutputs.isEmpty()) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
import com.sparrowwallet.drongo.OutputDescriptor;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||
@@ -20,6 +21,8 @@ import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.io.Device;
|
||||
import com.sparrowwallet.sparrow.io.Hwi;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -27,9 +30,14 @@ import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.util.Duration;
|
||||
import org.controlsfx.glyphfont.Glyph;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import tornadofx.control.Form;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -44,6 +52,9 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
|
||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
|
||||
@FXML
|
||||
private Form receiveForm;
|
||||
|
||||
@FXML
|
||||
private CopyableTextField address;
|
||||
|
||||
@@ -56,18 +67,45 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
@FXML
|
||||
private Label lastUsed;
|
||||
|
||||
@FXML
|
||||
private AnchorPane qrCodePane;
|
||||
|
||||
@FXML
|
||||
private ImageView qrCode;
|
||||
|
||||
@FXML
|
||||
private Form spReceiveForm;
|
||||
|
||||
@FXML
|
||||
private AnchorPane spCopyPane;
|
||||
|
||||
@FXML
|
||||
private TextArea spAddress;
|
||||
|
||||
@FXML
|
||||
private Form scriptPubKeyForm;
|
||||
|
||||
@FXML
|
||||
private ScriptArea scriptPubKeyArea;
|
||||
|
||||
@FXML
|
||||
private Form outputDescriptorForm;
|
||||
|
||||
@FXML
|
||||
private SelectableCodeArea outputDescriptor;
|
||||
|
||||
@FXML
|
||||
private Form spQrForm;
|
||||
|
||||
@FXML
|
||||
private ImageView spQrCode;
|
||||
|
||||
@FXML
|
||||
private Button displayAddress;
|
||||
|
||||
@FXML
|
||||
private Button nextAddress;
|
||||
|
||||
private NodeEntry currentEntry;
|
||||
|
||||
private QRDisplayDialog addressQrDialog;
|
||||
@@ -80,12 +118,39 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
@Override
|
||||
public void initializeView() {
|
||||
address.setSkin(new AddressTextFieldSkin(address));
|
||||
receiveForm.managedProperty().bind(receiveForm.visibleProperty());
|
||||
qrCodePane.managedProperty().bind(qrCodePane.visibleProperty());
|
||||
spReceiveForm.managedProperty().bind(spReceiveForm.visibleProperty());
|
||||
spCopyPane.managedProperty().bind(spCopyPane.visibleProperty());
|
||||
scriptPubKeyForm.managedProperty().bind(scriptPubKeyForm.visibleProperty());
|
||||
outputDescriptorForm.managedProperty().bind(outputDescriptorForm.visibleProperty());
|
||||
spQrForm.managedProperty().bind(spQrForm.visibleProperty());
|
||||
|
||||
qrCodePane.visibleProperty().bind(receiveForm.visibleProperty());
|
||||
spReceiveForm.visibleProperty().bind(receiveForm.visibleProperty().not());
|
||||
spCopyPane.visibleProperty().bind(receiveForm.visibleProperty().not());
|
||||
scriptPubKeyForm.visibleProperty().bind(receiveForm.visibleProperty());
|
||||
outputDescriptorForm.visibleProperty().bind(receiveForm.visibleProperty());
|
||||
spQrForm.visibleProperty().bind(receiveForm.visibleProperty().not());
|
||||
|
||||
updateFromWalletPolicy();
|
||||
|
||||
initializeScriptField(scriptPubKeyArea);
|
||||
|
||||
displayAddress.managedProperty().bind(displayAddress.visibleProperty());
|
||||
displayAddress.setVisible(false);
|
||||
|
||||
qrCode.setOnMouseClicked(event -> {
|
||||
nextAddress.managedProperty().bind(nextAddress.visibleProperty());
|
||||
|
||||
spAddress.setOnMouseClicked(event -> {
|
||||
copySilentPaymentsAddress(null);
|
||||
Tooltip tooltip = new Tooltip("Copied!");
|
||||
tooltip.show(spAddress, event.getScreenX(), event.getScreenY());
|
||||
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), e -> tooltip.hide()));
|
||||
timeline.play();
|
||||
});
|
||||
|
||||
qrCode.setOnMouseClicked(_ -> {
|
||||
if(currentEntry != null && addressQrDialog == null) {
|
||||
addressQrDialog = new QRDisplayDialog(currentEntry.getAddress().toString());
|
||||
addressQrDialog.initOwner(address.getScene().getWindow());
|
||||
@@ -94,9 +159,16 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
}
|
||||
});
|
||||
|
||||
spQrCode.setOnMouseClicked(_ -> copySilentPaymentsAddress(null));
|
||||
|
||||
refreshAddress();
|
||||
}
|
||||
|
||||
public void updateFromWalletPolicy() {
|
||||
receiveForm.setVisible(walletForm.getWallet().getPolicyType() != PolicyType.SINGLE_SP);
|
||||
nextAddress.setVisible(walletForm.getWallet().getPolicyType() != PolicyType.SINGLE_SP);
|
||||
}
|
||||
|
||||
public void setNodeEntry(NodeEntry nodeEntry) {
|
||||
if(currentEntry != null) {
|
||||
label.textProperty().unbindBidirectional(currentEntry.labelProperty());
|
||||
@@ -128,6 +200,10 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
}
|
||||
|
||||
private void updateLastUsed() {
|
||||
if(currentEntry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<BlockTransactionHashIndex> currentOutputs = currentEntry.getNode().getTransactionOutputs();
|
||||
if(AppServices.isConnected() && currentOutputs.isEmpty()) {
|
||||
lastUsed.setText("Never");
|
||||
@@ -192,6 +268,31 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
return null;
|
||||
}
|
||||
|
||||
public void copySilentPaymentsAddress(ActionEvent actionEvent) {
|
||||
if(walletForm.getWallet().getPolicyType() == PolicyType.SINGLE_SP) {
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(walletForm.getWallet().getKeystores().getFirst().getSilentPaymentScanAddress().getAddress());
|
||||
Clipboard.getSystemClipboard().setContent(content);
|
||||
}
|
||||
}
|
||||
|
||||
private Image getSilentPaymentsQrCode(String address) {
|
||||
try {
|
||||
QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
||||
BitMatrix qrMatrix = qrCodeWriter.encode(address, BarcodeFormat.QR_CODE, 400, 400, Map.of(EncodeHintType.MARGIN, 2));
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
MatrixToImageWriter.writeToStream(qrMatrix, "PNG", baos, new MatrixToImageConfig());
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
return new Image(bais);
|
||||
} catch(Exception e) {
|
||||
log.error("Error generating QR", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void getNewAddress(ActionEvent event) {
|
||||
refreshAddress();
|
||||
if(currentEntry != null) {
|
||||
@@ -200,6 +301,16 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
}
|
||||
|
||||
public void refreshAddress() {
|
||||
if(walletForm.getWallet().getPolicyType() == PolicyType.SINGLE_SP) {
|
||||
String silentPaymentAddress = walletForm.getWallet().getKeystores().getFirst().getSilentPaymentScanAddress().getAddress();
|
||||
spAddress.setText(silentPaymentAddress);
|
||||
Image qrImage = getSilentPaymentsQrCode(silentPaymentAddress);
|
||||
if(qrImage != null) {
|
||||
spQrCode.setImage(qrImage);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NodeEntry freshEntry = getWalletForm().getFreshNodeEntry(KeyPurpose.RECEIVE, currentEntry);
|
||||
while(freshEntry.getLabel() != null && !freshEntry.getLabel().isEmpty()) {
|
||||
freshEntry = getWalletForm().getFreshNodeEntry(KeyPurpose.RECEIVE, freshEntry);
|
||||
@@ -315,6 +426,7 @@ public class ReceiveController extends WalletFormController implements Initializ
|
||||
|
||||
@Subscribe
|
||||
public void walletAddressesChanged(WalletAddressesChangedEvent event) {
|
||||
updateFromWalletPolicy();
|
||||
displayAddress.setUserData(null);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user