implement changes to export addresses functionality

This commit is contained in:
Craig Raw 2021-04-06 09:27:51 +02:00
parent 6785bccf0e
commit 193f88b88f
7 changed files with 84 additions and 46 deletions

View file

@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.wallet;
import com.csvreader.CsvWriter; import com.csvreader.CsvWriter;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
@ -20,10 +21,12 @@ import java.io.*;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class AddressesController extends WalletFormController implements Initializable { public class AddressesController extends WalletFormController implements Initializable {
private static final Logger log = LoggerFactory.getLogger(AddressesController.class); private static final Logger log = LoggerFactory.getLogger(AddressesController.class);
public static final int DEFAULT_EXPORT_ADDRESSES_LENGTH = 250;
@FXML @FXML
private AddressTreeTable receiveTable; private AddressTreeTable receiveTable;
@ -96,26 +99,36 @@ public class AddressesController extends WalletFormController implements Initial
} }
public void exportReceiveAddresses(ActionEvent event) { public void exportReceiveAddresses(ActionEvent event) {
exportFile(); exportAddresses(KeyPurpose.RECEIVE);
} }
private void exportFile() { public void exportChangeAddresses(ActionEvent event) {
exportAddresses(KeyPurpose.CHANGE);
}
private void exportAddresses(KeyPurpose keyPurpose) {
Stage window = new Stage(); Stage window = new Stage();
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Export Addresses File"); fileChooser.setTitle("Export Addresses to CSV");
String extension = "txt"; fileChooser.setInitialFileName(getWalletForm().getWallet().getName() + "-" + keyPurpose.name().toLowerCase() + "-addresses.txt");
fileChooser.setInitialFileName(getWalletForm().getWallet().getName() + "-addresses.txt");
Wallet copy = getWalletForm().getWallet().copy();
WalletNode purposeNode = copy.getNode(keyPurpose);
purposeNode.fillToIndex(Math.max(purposeNode.getChildren().size(), DEFAULT_EXPORT_ADDRESSES_LENGTH));
File file = fileChooser.showSaveDialog(window); File file = fileChooser.showSaveDialog(window);
if(file != null) { if(file != null) {
try(FileOutputStream outputStream = new FileOutputStream(file)) { try(FileOutputStream outputStream = new FileOutputStream(file)) {
CsvWriter writer = new CsvWriter(outputStream, ',', StandardCharsets.UTF_8); CsvWriter writer = new CsvWriter(outputStream, ',', StandardCharsets.UTF_8);
writer.writeRecord(new String[] {"Index", "Payment Address"}); writer.writeRecord(new String[] {"Index", "Payment Address", "Derivation", "Label"});
for(Entry entry : getWalletForm().getNodeEntry(KeyPurpose.RECEIVE).getChildren()) { for(WalletNode indexNode : purposeNode.getChildren()) {
NodeEntry childEntry = (NodeEntry)entry; writer.write(Integer.toString(indexNode.getIndex()));
writer.write(childEntry.getNode().getIndex() + ""); writer.write(copy.getAddress(indexNode).toString());
writer.write(childEntry.getAddress().toString()); writer.write(getDerivationPath(indexNode));
Optional<Entry> optLabelEntry = getWalletForm().getNodeEntry(keyPurpose).getChildren().stream()
.filter(entry -> ((NodeEntry)entry).getNode().getIndex() == indexNode.getIndex()).findFirst();
writer.write(optLabelEntry.isPresent() ? optLabelEntry.get().getLabel() : "");
writer.endRecord(); writer.endRecord();
} }
writer.close(); writer.close();

View file

@ -117,20 +117,7 @@ public class ReceiveController extends WalletFormController implements Initializ
} }
private void updateDerivationPath(NodeEntry nodeEntry) { private void updateDerivationPath(NodeEntry nodeEntry) {
KeyDerivation firstDerivation = getWalletForm().getWallet().getKeystores().get(0).getKeyDerivation(); derivationPath.setText(getDerivationPath(nodeEntry.getNode()));
boolean singleDerivationPath = true;
for(Keystore keystore : getWalletForm().getWallet().getKeystores()) {
if(!keystore.getKeyDerivation().getDerivationPath().equals(firstDerivation.getDerivationPath())) {
singleDerivationPath = false;
break;
}
}
if(singleDerivationPath) {
derivationPath.setText(firstDerivation.extend(nodeEntry.getNode().getDerivation()).getDerivationPath());
} else {
derivationPath.setText(nodeEntry.getNode().getDerivationPath().replace("m", "multi"));
}
} }
private void updateLastUsed() { private void updateLastUsed() {

View file

@ -1,6 +1,9 @@
package com.sparrowwallet.sparrow.wallet; package com.sparrowwallet.sparrow.wallet;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.KeyDerivation;
import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.BaseController; import com.sparrowwallet.sparrow.BaseController;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.WalletTabData; import com.sparrowwallet.sparrow.WalletTabData;
@ -30,4 +33,24 @@ public abstract class WalletFormController extends BaseController {
} }
} }
} }
protected boolean isSingleDerivationPath() {
KeyDerivation firstDerivation = getWalletForm().getWallet().getKeystores().get(0).getKeyDerivation();
for(Keystore keystore : getWalletForm().getWallet().getKeystores()) {
if(!keystore.getKeyDerivation().getDerivationPath().equals(firstDerivation.getDerivationPath())) {
return false;
}
}
return true;
}
protected String getDerivationPath(WalletNode node) {
if(isSingleDerivationPath()) {
KeyDerivation firstDerivation = getWalletForm().getWallet().getKeystores().get(0).getKeyDerivation();
return firstDerivation.extend(node.getDerivation()).getDerivationPath();
}
return node.getDerivationPath().replace("m", "multi");
}
} }

View file

@ -172,3 +172,15 @@
.alert .content.label { .alert .content.label {
-fx-padding: 20px 20px 20px 20px; -fx-padding: 20px 20px 20px 20px;
} }
.icon-button {
-fx-border-style: none;
-fx-border-width: 0;
-fx-border-insets: 0;
-fx-background-color: transparent;
-fx-opacity: 0.7;
}
.icon-button:hover {
-fx-opacity: 1.0;
}

View file

@ -8,6 +8,8 @@
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import com.sparrowwallet.sparrow.control.AddressTreeTable?> <?import com.sparrowwallet.sparrow.control.AddressTreeTable?>
<?import org.controlsfx.glyphfont.Glyph?>
<GridPane hgap="10.0" vgap="10.0" stylesheets="@addresses.css, @wallet.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.AddressesController"> <GridPane hgap="10.0" vgap="10.0" stylesheets="@addresses.css, @wallet.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.wallet.AddressesController">
<padding> <padding>
<Insets left="25.0" right="25.0" top="15.0" bottom="25.0" /> <Insets left="25.0" right="25.0" top="15.0" bottom="25.0" />
@ -21,14 +23,17 @@
</rowConstraints> </rowConstraints>
<BorderPane GridPane.columnIndex="0" GridPane.rowIndex="0"> <BorderPane GridPane.columnIndex="0" GridPane.rowIndex="0">
<top> <top>
<BorderPane GridPane.columnIndex="0" GridPane.rowIndex="0"> <HBox alignment="CENTER_LEFT">
<left> <Label styleClass="addresses-treetable-label" text="Receive Addresses"/>
<Label styleClass="addresses-treetable-label" text="Receiving Addresses"/> <Button onAction="#exportReceiveAddresses" styleClass="icon-button">
</left> <graphic>
<right> <Glyph fontFamily="Font Awesome 5 Free Solid" icon="ARROW_CIRCLE_DOWN" fontSize="12" />
<Button fx:id="receiveExportButton" text="Export File..." onAction="#exportReceiveAddresses" /> </graphic>
</right> <tooltip>
</BorderPane> <Tooltip text="Export receive addresses as CSV" />
</tooltip>
</Button>
</HBox>
</top> </top>
<center> <center>
<AddressTreeTable fx:id="receiveTable" /> <AddressTreeTable fx:id="receiveTable" />
@ -36,7 +41,17 @@
</BorderPane> </BorderPane>
<BorderPane GridPane.columnIndex="0" GridPane.rowIndex="1"> <BorderPane GridPane.columnIndex="0" GridPane.rowIndex="1">
<top> <top>
<HBox alignment="CENTER_LEFT">
<Label styleClass="addresses-treetable-label" text="Change Addresses"/> <Label styleClass="addresses-treetable-label" text="Change Addresses"/>
<Button onAction="#exportChangeAddresses" styleClass="icon-button">
<graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" icon="ARROW_CIRCLE_DOWN" fontSize="12" />
</graphic>
<tooltip>
<Tooltip text="Export change addresses as CSV" />
</tooltip>
</Button>
</HBox>
</top> </top>
<center> <center>
<AddressTreeTable fx:id="changeTable" /> <AddressTreeTable fx:id="changeTable" />

View file

@ -23,15 +23,3 @@
.chart-line-symbol.selected { .chart-line-symbol.selected {
-fx-background-color: rgba(30, 136, 207, 0.6); -fx-background-color: rgba(30, 136, 207, 0.6);
} }
#exportCsv {
-fx-border-style: none;
-fx-border-width: 0;
-fx-border-insets: 0;
-fx-background-color: transparent;
-fx-opacity: 0.7;
}
#exportCsv:hover {
-fx-opacity: 1.0;
}

View file

@ -41,7 +41,7 @@
</Field> </Field>
<Field text="Transactions:"> <Field text="Transactions:">
<CopyableLabel fx:id="transactionCount" /> <CopyableLabel fx:id="transactionCount" />
<Button fx:id="exportCsv" maxHeight="25" onAction="#exportCSV" translateY="-1"> <Button fx:id="exportCsv" maxHeight="25" onAction="#exportCSV" translateY="-1" styleClass="icon-button">
<graphic> <graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" icon="ARROW_CIRCLE_DOWN" fontSize="12" /> <Glyph fontFamily="Font Awesome 5 Free Solid" icon="ARROW_CIRCLE_DOWN" fontSize="12" />
</graphic> </graphic>