mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
refactor import accordion, add wallet import
This commit is contained in:
parent
bc5690346c
commit
d516eaa9d6
21 changed files with 361 additions and 237 deletions
|
@ -14,6 +14,7 @@ import com.sparrowwallet.drongo.psbt.PSBT;
|
|||
import com.sparrowwallet.drongo.psbt.PSBTParseException;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.control.TextAreaDialog;
|
||||
import com.sparrowwallet.sparrow.control.WalletImportDialog;
|
||||
import com.sparrowwallet.sparrow.control.WalletNameDialog;
|
||||
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
||||
import com.sparrowwallet.sparrow.event.TabEvent;
|
||||
|
@ -257,6 +258,17 @@ public class AppController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
public void importWallet(ActionEvent event) {
|
||||
WalletImportDialog dlg = new WalletImportDialog();
|
||||
Optional<Wallet> optionalWallet = dlg.showAndWait();
|
||||
if(optionalWallet.isPresent()) {
|
||||
Wallet wallet = optionalWallet.get();
|
||||
File walletFile = Storage.getStorage().getWalletFile(wallet.getName());
|
||||
Tab tab = addWalletTab(walletFile, null, wallet);
|
||||
tabs.getSelectionModel().select(tab);
|
||||
}
|
||||
}
|
||||
|
||||
public Tab addWalletTab(File walletFile, ECKey encryptionPubKey, Wallet wallet) {
|
||||
try {
|
||||
String name = walletFile.getName();
|
||||
|
|
|
@ -59,7 +59,7 @@ public class DevicePane extends TitledPane {
|
|||
setPadding(Insets.EMPTY);
|
||||
|
||||
setGraphic(getTitle());
|
||||
getStyleClass().add("devicepane");
|
||||
getStyleClass().add("titled-description-pane");
|
||||
setDefaultStatus();
|
||||
|
||||
removeArrow();
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||
import com.sparrowwallet.sparrow.io.FileImport;
|
||||
import com.sparrowwallet.sparrow.io.ImportException;
|
||||
import com.sparrowwallet.sparrow.io.KeystoreFileImport;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
import org.controlsfx.control.textfield.CustomPasswordField;
|
||||
import org.controlsfx.control.textfield.TextFields;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
public abstract class FileImportPane extends TitledDescriptionPane {
|
||||
private final FileImport importer;
|
||||
private Button importButton;
|
||||
private final SimpleStringProperty password = new SimpleStringProperty("");
|
||||
|
||||
public FileImportPane(FileImport importer, String title, String description, String content, String imageUrl) {
|
||||
super(title, description, content, imageUrl);
|
||||
this.importer = importer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Control createButton() {
|
||||
importButton = new Button("Import File...");
|
||||
importButton.setAlignment(Pos.CENTER_RIGHT);
|
||||
importButton.setOnAction(event -> {
|
||||
importFile();
|
||||
});
|
||||
return importButton;
|
||||
}
|
||||
|
||||
private void importFile() {
|
||||
Stage window = new Stage();
|
||||
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore");
|
||||
fileChooser.getExtensionFilters().addAll(
|
||||
new FileChooser.ExtensionFilter("All Files", "*.*"),
|
||||
new FileChooser.ExtensionFilter("JSON", "*.json")
|
||||
);
|
||||
|
||||
File file = fileChooser.showOpenDialog(window);
|
||||
if(file != null) {
|
||||
importFile(file, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void importFile(File file, String password) {
|
||||
if(file.exists()) {
|
||||
try {
|
||||
if(importer.isEncrypted(file) && password == null) {
|
||||
setDescription("Password Required");
|
||||
showHideLink.setVisible(false);
|
||||
setContent(getPasswordEntry(file));
|
||||
importButton.setDisable(true);
|
||||
setExpanded(true);
|
||||
} else {
|
||||
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||
importFile(file.getName(), inputStream, password);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String errorMessage = e.getMessage();
|
||||
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
|
||||
errorMessage = e.getCause().getMessage();
|
||||
}
|
||||
if(e instanceof InvalidPasswordException || e.getCause() instanceof InvalidPasswordException) {
|
||||
errorMessage = "Invalid wallet password";
|
||||
}
|
||||
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) {
|
||||
errorMessage = "File was not in JSON format";
|
||||
}
|
||||
setError("Import Error", errorMessage);
|
||||
importButton.setDisable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void importFile(String fileName, InputStream inputStream, String password) throws ImportException;
|
||||
|
||||
private Node getPasswordEntry(File file) {
|
||||
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField();
|
||||
passwordField.setPromptText("Wallet password");
|
||||
password.bind(passwordField.textProperty());
|
||||
HBox.setHgrow(passwordField, Priority.ALWAYS);
|
||||
|
||||
Button importEncryptedButton = new Button("Import");
|
||||
importEncryptedButton.setOnAction(event -> {
|
||||
showHideLink.setVisible(true);
|
||||
setExpanded(false);
|
||||
importFile(file, password.get());
|
||||
});
|
||||
|
||||
HBox contentBox = new HBox();
|
||||
contentBox.setAlignment(Pos.TOP_RIGHT);
|
||||
contentBox.setSpacing(20);
|
||||
contentBox.getChildren().add(passwordField);
|
||||
contentBox.getChildren().add(importEncryptedButton);
|
||||
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
||||
contentBox.setPrefHeight(60);
|
||||
|
||||
return contentBox;
|
||||
}
|
||||
}
|
|
@ -1,121 +1,26 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||
import com.sparrowwallet.sparrow.io.ImportException;
|
||||
import com.sparrowwallet.sparrow.io.KeystoreFileImport;
|
||||
import com.sparrowwallet.sparrow.io.KeystoreImport;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
import org.controlsfx.control.textfield.CustomPasswordField;
|
||||
import org.controlsfx.control.textfield.TextFields;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class FileKeystoreImportPane extends KeystoreImportPane {
|
||||
public class FileKeystoreImportPane extends FileImportPane {
|
||||
protected final Wallet wallet;
|
||||
private final KeystoreFileImport importer;
|
||||
private Button importButton;
|
||||
private final SimpleStringProperty password = new SimpleStringProperty("");
|
||||
|
||||
public FileKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreFileImport importer) {
|
||||
super(importAccordion, wallet, importer);
|
||||
public FileKeystoreImportPane(Wallet wallet, KeystoreFileImport importer) {
|
||||
super(importer, importer.getName(), "Keystore file import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png");
|
||||
this.wallet = wallet;
|
||||
this.importer = importer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node getTitle(KeystoreImport importer) {
|
||||
Node title = super.getTitle(importer);
|
||||
|
||||
setDescription("Keystore file import");
|
||||
|
||||
importButton = new Button("Import File...");
|
||||
importButton.setAlignment(Pos.CENTER_RIGHT);
|
||||
importButton.setOnAction(event -> {
|
||||
importFile();
|
||||
});
|
||||
buttonBox.getChildren().add(importButton);
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private void importFile() {
|
||||
Stage window = new Stage();
|
||||
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore");
|
||||
fileChooser.getExtensionFilters().addAll(
|
||||
new FileChooser.ExtensionFilter("All Files", "*.*"),
|
||||
new FileChooser.ExtensionFilter("JSON", "*.json")
|
||||
);
|
||||
|
||||
File file = fileChooser.showOpenDialog(window);
|
||||
if(file != null) {
|
||||
importFile(file, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void importFile(File file, String password) {
|
||||
if(file.exists()) {
|
||||
try {
|
||||
if(importer.isEncrypted(file) && password == null) {
|
||||
setDescription("Password Required");
|
||||
showHideLink.setVisible(false);
|
||||
setContent(getPasswordEntry(file));
|
||||
importButton.setDisable(true);
|
||||
setExpanded(true);
|
||||
} else {
|
||||
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException {
|
||||
Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password);
|
||||
EventManager.get().post(new KeystoreImportEvent(keystore));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String errorMessage = e.getMessage();
|
||||
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
|
||||
errorMessage = e.getCause().getMessage();
|
||||
}
|
||||
if(e instanceof InvalidPasswordException || e.getCause() instanceof InvalidPasswordException) {
|
||||
errorMessage = "Invalid wallet password";
|
||||
}
|
||||
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) {
|
||||
errorMessage = "File was not in JSON format";
|
||||
}
|
||||
setError("Import Error", errorMessage);
|
||||
importButton.setDisable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Node getPasswordEntry(File file) {
|
||||
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField();
|
||||
passwordField.setPromptText("Wallet password");
|
||||
password.bind(passwordField.textProperty());
|
||||
HBox.setHgrow(passwordField, Priority.ALWAYS);
|
||||
|
||||
Button importEncryptedButton = new Button("Import");
|
||||
importEncryptedButton.setOnAction(event -> {
|
||||
showHideLink.setVisible(true);
|
||||
setExpanded(false);
|
||||
importFile(file, password.get());
|
||||
});
|
||||
|
||||
HBox contentBox = new HBox();
|
||||
contentBox.setAlignment(Pos.TOP_RIGHT);
|
||||
contentBox.setSpacing(20);
|
||||
contentBox.getChildren().add(passwordField);
|
||||
contentBox.getChildren().add(importEncryptedButton);
|
||||
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
||||
contentBox.setPrefHeight(60);
|
||||
|
||||
return contentBox;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.WalletImportEvent;
|
||||
import com.sparrowwallet.sparrow.io.ImportException;
|
||||
import com.sparrowwallet.sparrow.io.WalletImport;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FileWalletImportPane extends FileImportPane {
|
||||
private final WalletImport importer;
|
||||
|
||||
public FileWalletImportPane(WalletImport importer) {
|
||||
super(importer, importer.getName(), "Wallet file import", importer.getWalletImportDescription(), "image/" + importer.getWalletModel().getType() + ".png");
|
||||
this.importer = importer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void importFile(String fileName, InputStream inputStream, String password) throws ImportException {
|
||||
Wallet wallet = importer.importWallet(inputStream, password);
|
||||
if(wallet.getName() == null) {
|
||||
wallet.setName(fileName);
|
||||
}
|
||||
EventManager.get().post(new WalletImportEvent(wallet));
|
||||
}
|
||||
}
|
|
@ -16,12 +16,12 @@ public class KeystoreImportAccordion extends Accordion {
|
|||
this.importers = importers;
|
||||
|
||||
for(KeystoreImport importer : importers) {
|
||||
KeystoreImportPane importPane = null;
|
||||
TitledDescriptionPane importPane = null;
|
||||
|
||||
if(importer instanceof KeystoreFileImport) {
|
||||
importPane = new FileKeystoreImportPane(this, wallet, (KeystoreFileImport)importer);
|
||||
importPane = new FileKeystoreImportPane(wallet, (KeystoreFileImport)importer);
|
||||
} else if(importer instanceof KeystoreMnemonicImport) {
|
||||
importPane = new MnemonicKeystoreImportPane(this, wallet, (KeystoreMnemonicImport)importer);
|
||||
importPane = new MnemonicKeystoreImportPane(wallet, (KeystoreMnemonicImport)importer);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not create ImportPane for importer of type " + importer.getClass());
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||
public class MnemonicKeystoreImportPane extends TitledDescriptionPane {
|
||||
protected final Wallet wallet;
|
||||
private final KeystoreMnemonicImport importer;
|
||||
|
||||
private SplitMenuButton enterMnemonicButton;
|
||||
|
@ -48,21 +49,19 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
private SimpleListProperty<String> wordEntriesProperty;
|
||||
private final SimpleStringProperty passphraseProperty = new SimpleStringProperty();
|
||||
|
||||
public MnemonicKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreMnemonicImport importer) {
|
||||
super(importAccordion, wallet, importer);
|
||||
public MnemonicKeystoreImportPane(Wallet wallet, KeystoreMnemonicImport importer) {
|
||||
super(importer.getName(), "Mnemonic import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png");
|
||||
this.wallet = wallet;
|
||||
this.importer = importer;
|
||||
|
||||
createImportButton();
|
||||
buttonBox.getChildren().add(importButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node getTitle(KeystoreImport importer) {
|
||||
Node title = super.getTitle(importer);
|
||||
setDescription("Keystore file import");
|
||||
|
||||
protected Control createButton() {
|
||||
createEnterMnemonicButton();
|
||||
createImportButton();
|
||||
buttonBox.getChildren().addAll(enterMnemonicButton, importButton);
|
||||
|
||||
return title;
|
||||
return enterMnemonicButton;
|
||||
}
|
||||
|
||||
private void createEnterMnemonicButton() {
|
||||
|
@ -103,8 +102,6 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
|||
importButton.getItems().add(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
importButton.managedProperty().bind(importButton.visibleProperty());
|
||||
importButton.setVisible(false);
|
||||
}
|
||||
|
|
|
@ -1,53 +1,33 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.io.KeystoreImport;
|
||||
import com.sparrowwallet.sparrow.AppController;
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TitledPane;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
public abstract class KeystoreImportPane extends TitledPane {
|
||||
protected final KeystoreImportAccordion importAccordion;
|
||||
protected final Wallet wallet;
|
||||
|
||||
private Label mainLabel;
|
||||
public class TitledDescriptionPane extends TitledPane {
|
||||
private Label descriptionLabel;
|
||||
protected Hyperlink showHideLink;
|
||||
protected HBox buttonBox;
|
||||
|
||||
public KeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreImport importer) {
|
||||
this.importAccordion = importAccordion;
|
||||
this.wallet = wallet;
|
||||
public TitledDescriptionPane(String title, String description, String content, String imageUrl) {
|
||||
getStylesheets().add(AppController.class.getResource("general.css").toExternalForm());
|
||||
getStyleClass().add("titled-description-pane");
|
||||
|
||||
setPadding(Insets.EMPTY);
|
||||
setGraphic(getTitle(importer));
|
||||
getStyleClass().add("importpane");
|
||||
setContent(getContentBox(importer.getKeystoreImportDescription()));
|
||||
setGraphic(getTitle(title, description, imageUrl));
|
||||
setContent(getContentBox(content));
|
||||
removeArrow();
|
||||
}
|
||||
|
||||
private void removeArrow() {
|
||||
Platform.runLater(() -> {
|
||||
Node arrow = this.lookup(".arrow");
|
||||
if (arrow != null) {
|
||||
arrow.setVisible(false);
|
||||
arrow.setManaged(false);
|
||||
} else {
|
||||
removeArrow();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected Node getTitle(KeystoreImport importer) {
|
||||
protected Node getTitle(String title, String description, String imageUrl) {
|
||||
HBox listItem = new HBox();
|
||||
listItem.setPadding(new Insets(10, 20, 10, 10));
|
||||
listItem.setSpacing(10);
|
||||
|
@ -57,7 +37,7 @@ public abstract class KeystoreImportPane extends TitledPane {
|
|||
imageBox.setMinHeight(50);
|
||||
listItem.getChildren().add(imageBox);
|
||||
|
||||
Image image = new Image("image/" + importer.getWalletModel().getType() + ".png", 50, 50, true, true);
|
||||
Image image = new Image(imageUrl, 50, 50, true, true);
|
||||
if (!image.isError()) {
|
||||
ImageView imageView = new ImageView();
|
||||
imageView.setImage(image);
|
||||
|
@ -67,8 +47,8 @@ public abstract class KeystoreImportPane extends TitledPane {
|
|||
VBox labelsBox = new VBox();
|
||||
labelsBox.setSpacing(5);
|
||||
labelsBox.setAlignment(Pos.CENTER_LEFT);
|
||||
this.mainLabel = new Label();
|
||||
mainLabel.setText(importer.getName());
|
||||
Label mainLabel = new Label();
|
||||
mainLabel.setText(title);
|
||||
mainLabel.getStyleClass().add("main-label");
|
||||
labelsBox.getChildren().add(mainLabel);
|
||||
|
||||
|
@ -76,16 +56,12 @@ public abstract class KeystoreImportPane extends TitledPane {
|
|||
descriptionBox.setSpacing(7);
|
||||
labelsBox.getChildren().add(descriptionBox);
|
||||
|
||||
descriptionLabel = new Label("Keystore Import");
|
||||
descriptionLabel = new Label(description);
|
||||
descriptionLabel.getStyleClass().add("description-label");
|
||||
showHideLink = new Hyperlink("Show Details...");
|
||||
showHideLink.managedProperty().bind(showHideLink.visibleProperty());
|
||||
showHideLink.setOnAction(event -> {
|
||||
if(this.isExpanded()) {
|
||||
setExpanded(false);
|
||||
} else {
|
||||
setExpanded(true);
|
||||
}
|
||||
setExpanded(!this.isExpanded());
|
||||
});
|
||||
this.expandedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(newValue) {
|
||||
|
@ -101,6 +77,10 @@ public abstract class KeystoreImportPane extends TitledPane {
|
|||
|
||||
buttonBox = new HBox();
|
||||
buttonBox.setAlignment(Pos.CENTER_RIGHT);
|
||||
Control button = createButton();
|
||||
if(button != null) {
|
||||
buttonBox.getChildren().add(button);
|
||||
}
|
||||
listItem.getChildren().add(buttonBox);
|
||||
|
||||
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
|
@ -111,6 +91,11 @@ public abstract class KeystoreImportPane extends TitledPane {
|
|||
return listItem;
|
||||
}
|
||||
|
||||
protected Control createButton() {
|
||||
//No buttons by default
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void setDescription(String text) {
|
||||
descriptionLabel.getStyleClass().remove("description-error");
|
||||
descriptionLabel.getStyleClass().add("description-label");
|
||||
|
@ -141,4 +126,16 @@ public abstract class KeystoreImportPane extends TitledPane {
|
|||
|
||||
return contentBox;
|
||||
}
|
||||
|
||||
private void removeArrow() {
|
||||
Platform.runLater(() -> {
|
||||
Node arrow = this.lookup(".arrow");
|
||||
if (arrow != null) {
|
||||
arrow.setVisible(false);
|
||||
arrow.setManaged(false);
|
||||
} else {
|
||||
removeArrow();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.WalletImportEvent;
|
||||
import com.sparrowwallet.sparrow.io.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WalletImportDialog extends Dialog<Wallet> {
|
||||
private Wallet wallet;
|
||||
|
||||
public WalletImportDialog() {
|
||||
EventManager.get().register(this);
|
||||
|
||||
final DialogPane dialogPane = getDialogPane();
|
||||
|
||||
StackPane stackPane = new StackPane();
|
||||
dialogPane.setContent(stackPane);
|
||||
|
||||
AnchorPane anchorPane = new AnchorPane();
|
||||
stackPane.getChildren().add(anchorPane);
|
||||
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
scrollPane.setPrefWidth(500);
|
||||
scrollPane.setPrefHeight(500);
|
||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
anchorPane.getChildren().add(scrollPane);
|
||||
scrollPane.setFitToWidth(true);
|
||||
AnchorPane.setLeftAnchor(scrollPane, 0.0);
|
||||
AnchorPane.setRightAnchor(scrollPane, 0.0);
|
||||
|
||||
List<WalletImport> importers = List.of(new ColdcardMultisig(), new Electrum());
|
||||
Accordion importAccordion = new Accordion();
|
||||
for(WalletImport importer : importers) {
|
||||
FileWalletImportPane importPane = new FileWalletImportPane(importer);
|
||||
importAccordion.getPanes().add(importPane);
|
||||
}
|
||||
scrollPane.setContent(importAccordion);
|
||||
|
||||
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||
dialogPane.getButtonTypes().addAll(cancelButtonType);
|
||||
dialogPane.setPrefWidth(650);
|
||||
dialogPane.setPrefHeight(600);
|
||||
|
||||
setResultConverter(dialogButton -> dialogButton != cancelButtonType ? wallet : null);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletImported(WalletImportEvent event) {
|
||||
wallet = event.getWallet();
|
||||
setResult(wallet);
|
||||
this.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
public class WalletImportEvent {
|
||||
private Wallet wallet;
|
||||
|
||||
public WalletImportEvent(Wallet wallet) {
|
||||
this.wallet = wallet;
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImport, WalletExport {
|
||||
public class ColdcardMultisig implements WalletImport, KeystoreFileImport, WalletExport {
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
@Override
|
||||
|
@ -125,6 +125,10 @@ public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImpor
|
|||
wallet.setDefaultPolicy(policy);
|
||||
wallet.setScriptType(scriptType);
|
||||
|
||||
if(!wallet.isValid()) {
|
||||
throw new IllegalStateException("This file does not describe a valid wallet. Please use the Settings > Multisig Wallets > Export XPUB feature on your Coldcard.");
|
||||
}
|
||||
|
||||
return wallet;
|
||||
} catch(Exception e) {
|
||||
throw new ImportException(e);
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.Map;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public class Electrum implements KeystoreFileImport, SinglesigWalletImport, MultisigWalletImport, WalletExport {
|
||||
public class Electrum implements KeystoreFileImport, WalletImport, WalletExport {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Electrum";
|
||||
|
@ -131,7 +131,7 @@ public class Electrum implements KeystoreFileImport, SinglesigWalletImport, Mult
|
|||
}
|
||||
|
||||
if(!wallet.isValid()) {
|
||||
throw new IllegalStateException("Electrum wallet is in an inconsistent state");
|
||||
throw new IllegalStateException("Electrum wallet is in an inconsistent state.");
|
||||
}
|
||||
|
||||
return wallet;
|
||||
|
@ -145,15 +145,6 @@ public class Electrum implements KeystoreFileImport, SinglesigWalletImport, Mult
|
|||
return "Import an Electrum wallet";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wallet importWallet(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
|
||||
Wallet wallet = importWallet(inputStream, password);
|
||||
wallet.setScriptType(scriptType);
|
||||
//TODO: Check this usage results in a valid wallet
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface FileImport extends Import {
|
||||
boolean isEncrypted(File file);
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||
|
||||
public interface Import {
|
||||
String getName();
|
||||
WalletModel getWalletModel();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.sparrowwallet.drongo.wallet.Keystore;
|
|||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface KeystoreFileImport extends KeystoreImport {
|
||||
boolean isEncrypted(File file);
|
||||
public interface KeystoreFileImport extends KeystoreImport, FileImport {
|
||||
Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,5 @@ import com.sparrowwallet.drongo.policy.PolicyType;
|
|||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||
|
||||
public interface KeystoreImport extends Import {
|
||||
WalletModel getWalletModel();
|
||||
String getKeystoreImportDescription();
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface SinglesigWalletImport extends Import {
|
||||
String getWalletImportDescription();
|
||||
Wallet importWallet(ScriptType scriptType, InputStream inputStream, String password) throws ImportException;
|
||||
boolean isEncrypted(File file);
|
||||
}
|
|
@ -5,8 +5,7 @@ import com.sparrowwallet.drongo.wallet.Wallet;
|
|||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface MultisigWalletImport extends Import {
|
||||
public interface WalletImport extends Import, FileImport {
|
||||
String getWalletImportDescription();
|
||||
Wallet importWallet(InputStream inputStream, String password) throws ImportException;
|
||||
boolean isEncrypted(File file);
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
<Menu mnemonicParsing="false" text="File">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" text="New Wallet" onAction="#newWallet"/>
|
||||
<MenuItem mnemonicParsing="false" text="Open Wallet" onAction="#openWallet"/>
|
||||
<MenuItem mnemonicParsing="false" text="Open Wallet..." onAction="#openWallet"/>
|
||||
<Menu mnemonicParsing="false" text="Open Transaction">
|
||||
<items>
|
||||
<MenuItem text="File..." onAction="#openFromFile"/>
|
||||
|
@ -21,6 +21,7 @@
|
|||
<MenuItem text="Examples" onAction="#openExamples"/>
|
||||
</items>
|
||||
</Menu>
|
||||
<MenuItem mnemonicParsing="false" text="Import Wallet..." onAction="#importWallet"/>
|
||||
<MenuItem mnemonicParsing="false" text="Close" onAction="#closeTab"/>
|
||||
</items>
|
||||
</Menu>
|
||||
|
|
|
@ -50,3 +50,51 @@
|
|||
-fx-padding: 20;
|
||||
}
|
||||
|
||||
.titled-description-pane > .title {
|
||||
-fx-background-color: white;
|
||||
-fx-padding: 0;
|
||||
-fx-border-color: #e5e5e6;
|
||||
/*-fx-border-width: 1;*/
|
||||
}
|
||||
|
||||
.titled-description-pane > .title > .arrow-button {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.titled-description-pane > .title > .arrow-button > .arrow {
|
||||
visibility: hidden;
|
||||
-fx-translate-x: -1000;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.titled-description-pane .main-label .text {
|
||||
|
||||
}
|
||||
|
||||
.titled-description-pane .status-label .text, .titled-description-pane .description-label .text {
|
||||
-fx-fill: #a0a1a7;
|
||||
}
|
||||
|
||||
.titled-description-pane .status-error .text, .titled-description-pane .description-error .text {
|
||||
-fx-fill: #ca1243;
|
||||
}
|
||||
|
||||
.titled-description-pane .description-label, .titled-description-pane .description-error {
|
||||
-fx-border-width: 0px;
|
||||
-fx-border-color: transparent;
|
||||
}
|
||||
|
||||
.titled-description-pane .hyperlink {
|
||||
-fx-padding: 0;
|
||||
-fx-border-width: 0;
|
||||
-fx-fill: #1e88cf;
|
||||
}
|
||||
|
||||
.titled-description-pane .hyperlink:visited {
|
||||
-fx-text-fill: #1e88cf;
|
||||
-fx-underline: false;
|
||||
}
|
||||
|
||||
.titled-description-pane .hyperlink:hover:visited {
|
||||
-fx-underline: true;
|
||||
}
|
||||
|
|
|
@ -33,52 +33,5 @@
|
|||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.titled-pane > .title {
|
||||
-fx-background-color: white;
|
||||
-fx-padding: 0;
|
||||
-fx-border-color: #e5e5e6;
|
||||
/*-fx-border-width: 1;*/
|
||||
}
|
||||
|
||||
.titled-pane > .title > .arrow-button {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.titled-pane > .title > .arrow-button > .arrow {
|
||||
visibility: hidden;
|
||||
-fx-translate-x: -1000;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.main-label .text {
|
||||
|
||||
}
|
||||
|
||||
.status-label .text, .description-label .text {
|
||||
-fx-fill: #a0a1a7;
|
||||
}
|
||||
|
||||
.status-error .text, .description-error .text {
|
||||
-fx-fill: #ca1243;
|
||||
}
|
||||
|
||||
.description-label, .description-error {
|
||||
-fx-border-width: 0px;
|
||||
-fx-border-color: transparent;
|
||||
}
|
||||
|
||||
.hyperlink {
|
||||
-fx-padding: 0;
|
||||
-fx-border-width: 0;
|
||||
-fx-fill: #1e88cf;
|
||||
}
|
||||
|
||||
.hyperlink:visited {
|
||||
-fx-text-fill: #1e88cf;
|
||||
-fx-underline: false;
|
||||
}
|
||||
|
||||
.hyperlink:hover:visited {
|
||||
-fx-underline: true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue