diff --git a/build.gradle b/build.gradle index c63ec6cb..1a99ef73 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { exclude group: 'junit' } implementation('com.google.guava:guava:28.2-jre') + implementation('com.google.code.gson:gson:2.8.6') implementation('org.fxmisc.richtext:richtextfx:0.10.4') implementation('no.tornado:tornadofx-controls:1.0.4') implementation('org.controlsfx:controlsfx:11.0.1' ) { diff --git a/drongo b/drongo index 97cdd621..81378190 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 97cdd6217317d4f1a2238ca7d2c8161cb8534e10 +Subproject commit 813781902b8914fbac20c5a36ef230a44639ecbc diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index f0788ce1..9217fb33 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -4,6 +4,8 @@ import com.google.common.base.Charsets; import com.google.common.eventbus.Subscribe; import com.google.common.io.ByteSource; import com.sparrowwallet.drongo.Utils; +import com.sparrowwallet.drongo.policy.PolicyType; +import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBTParseException; @@ -12,9 +14,12 @@ import com.sparrowwallet.sparrow.control.TextAreaDialog; import com.sparrowwallet.sparrow.event.TabEvent; import com.sparrowwallet.sparrow.event.TransactionTabChangedEvent; import com.sparrowwallet.sparrow.event.TransactionTabSelectedEvent; +import com.sparrowwallet.sparrow.storage.Storage; import com.sparrowwallet.sparrow.transaction.TransactionController; import com.sparrowwallet.sparrow.wallet.WalletController; import com.sparrowwallet.sparrow.wallet.WalletForm; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; @@ -25,6 +30,10 @@ import javafx.scene.input.TransferMode; import javafx.scene.layout.StackPane; import javafx.stage.FileChooser; import javafx.stage.Stage; +import org.controlsfx.validation.ValidationResult; +import org.controlsfx.validation.ValidationSupport; +import org.controlsfx.validation.Validator; +import org.controlsfx.validation.decoration.StyleClassValidationDecoration; import java.io.*; import java.net.URL; @@ -92,7 +101,7 @@ public class AppController implements Initializable { showTxHex.setSelected(true); showTxHexProperty = true; - addWalletTab(null, new Wallet()); + //addWalletTab("newWallet", new Wallet(PolicyType.SINGLE, ScriptType.P2WPKH)); } public void openFromFile(ActionEvent event) { @@ -179,7 +188,7 @@ public class AppController implements Initializable { } } - private void showErrorDialog(String title, String content) { + public static void showErrorDialog(String title, String content) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle(title); alert.setHeaderText(title); @@ -198,18 +207,53 @@ public class AppController implements Initializable { } public void newWallet(ActionEvent event) { - Tab tab = addWalletTab(null, new Wallet()); - tabs.getSelectionModel().select(tab); + TextInputDialog dlg = new TextInputDialog(""); + dlg.setTitle("New Wallet"); + dlg.getDialogPane().setContentText("Wallet name:"); + dlg.getDialogPane().getStylesheets().add(getClass().getResource("general.css").toExternalForm()); + + ValidationSupport validationSupport = new ValidationSupport(); + validationSupport.registerValidator(dlg.getEditor(), Validator.combine( + Validator.createEmptyValidator("Wallet name is required"), + (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Wallet name is not unique", Storage.getStorage().getWalletFile(newValue).exists()) + )); + validationSupport.setValidationDecorator(new StyleClassValidationDecoration()); + + Button okButton = (Button)dlg.getDialogPane().lookupButton(ButtonType.OK); + BooleanBinding isInvalid = Bindings.createBooleanBinding(() -> + dlg.getEditor().getText().length() == 0 || Storage.getStorage().getWalletFile(dlg.getEditor().getText()).exists(), dlg.getEditor().textProperty()); + okButton.disableProperty().bind(isInvalid); + + Optional walletName = dlg.showAndWait(); + if(walletName.isPresent()) { + File walletFile = Storage.getStorage().getWalletFile(walletName.get()); + Wallet wallet = new Wallet(PolicyType.SINGLE, ScriptType.P2WPKH); + Tab tab = addWalletTab(walletFile, wallet); + tabs.getSelectionModel().select(tab); + } } - public Tab addWalletTab(String name, Wallet wallet) { - try { - String tabName = name; - if(tabName == null || tabName.isEmpty()) { - tabName = "New wallet"; - } + public void openWallet(ActionEvent event) { + Stage window = new Stage(); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Open Wallet"); + fileChooser.setInitialDirectory(Storage.getStorage().getWalletsDir()); - Tab tab = new Tab(tabName); + File file = fileChooser.showOpenDialog(window); + if(file != null) { + try { + Wallet wallet = Storage.getStorage().loadWallet(file); + Tab tab = addWalletTab(file, wallet); + tabs.getSelectionModel().select(tab); + } catch (IOException e) { + showErrorDialog("Error opening wallet", e.getMessage()); + } + } + } + + public Tab addWalletTab(File walletFile, Wallet wallet) { + try { + Tab tab = new Tab(walletFile.getName()); TabData tabData = new TabData(TabData.TabType.WALLET); tab.setUserData(tabData); tab.setContextMenu(getTabContextMenu(tab)); @@ -217,7 +261,7 @@ public class AppController implements Initializable { FXMLLoader walletLoader = new FXMLLoader(getClass().getResource("wallet/wallet.fxml")); tab.setContent(walletLoader.load()); WalletController controller = walletLoader.getController(); - WalletForm walletForm = new WalletForm(wallet); + WalletForm walletForm = new WalletForm(walletFile, wallet); controller.setWalletForm(walletForm); tabs.getTabs().add(tab); diff --git a/src/main/java/com/sparrowwallet/sparrow/event/SettingsChangedEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/SettingsChangedEvent.java new file mode 100644 index 00000000..89b4a327 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/event/SettingsChangedEvent.java @@ -0,0 +1,15 @@ +package com.sparrowwallet.sparrow.event; + +import com.sparrowwallet.drongo.wallet.Wallet; + +public class SettingsChangedEvent { + private Wallet wallet; + + public SettingsChangedEvent(Wallet wallet) { + this.wallet = wallet; + } + + public Wallet getWallet() { + return wallet; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/storage/Storage.java b/src/main/java/com/sparrowwallet/sparrow/storage/Storage.java new file mode 100644 index 00000000..1f96bd93 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/storage/Storage.java @@ -0,0 +1,81 @@ +package com.sparrowwallet.sparrow.storage; + +import com.google.gson.*; +import com.sparrowwallet.drongo.ExtendedPublicKey; +import com.sparrowwallet.drongo.wallet.Wallet; + +import java.io.*; +import java.lang.reflect.Type; + +public class Storage { + public static final String SPARROW_DIR = ".sparrow"; + public static final String WALLETS_DIR = "wallets"; + + private static Storage SINGLETON; + + private final Gson gson; + + private Storage() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(ExtendedPublicKey.class, new ExtendedPublicKeySerializer()); + gsonBuilder.registerTypeAdapter(ExtendedPublicKey.class, new ExtendedPublicKeyDeserializer()); + gson = gsonBuilder.setPrettyPrinting().create(); + } + + public static Storage getStorage() { + if(SINGLETON == null) { + SINGLETON = new Storage(); + } + + return SINGLETON; + } + + public Wallet loadWallet(File file) throws IOException { + Reader reader = new FileReader(file); + Wallet wallet = gson.fromJson(reader, Wallet.class); + reader.close(); + + return wallet; + } + + public void storeWallet(File file, Wallet wallet) throws IOException { + File parent = file.getParentFile(); + if(!parent.exists() && !parent.mkdirs()) { + throw new IOException("Could not create folder " + parent); + } + + Writer writer = new FileWriter(file); + gson.toJson(wallet, writer); + writer.close(); + } + + public File getWalletFile(String walletName) { + return new File(getWalletsDir(), walletName); + } + + public File getWalletsDir() { + return new File(getSparrowDir(), WALLETS_DIR); + } + + private File getSparrowDir() { + return new File(getHomeDir(), SPARROW_DIR); + } + + private File getHomeDir() { + return new File(System.getProperty("user.home")); + } + + private static class ExtendedPublicKeySerializer implements JsonSerializer { + @Override + public JsonElement serialize(ExtendedPublicKey src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.toString()); + } + } + + private static class ExtendedPublicKeyDeserializer implements JsonDeserializer { + @Override + public ExtendedPublicKey deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return ExtendedPublicKey.fromDescriptor(json.getAsJsonPrimitive().getAsString()); + } + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java index 4ac86679..3bf50aa9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java @@ -4,9 +4,9 @@ import com.sparrowwallet.drongo.ExtendedPublicKey; import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.wallet.Keystore; +import com.sparrowwallet.sparrow.EventManager; +import com.sparrowwallet.sparrow.event.SettingsChangedEvent; import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Control; @@ -36,7 +36,7 @@ public class KeystoreController extends WalletFormController implements Initiali @FXML private TextField fingerprint; - private ValidationSupport validationSupport; + private ValidationSupport validationSupport = new ValidationSupport(); @Override public void initialize(URL location, ResourceBundle resources) { @@ -61,21 +61,41 @@ public class KeystoreController extends WalletFormController implements Initiali if(keystore.getKeyDerivation() != null) { derivation.setText(keystore.getKeyDerivation().getDerivationPath()); fingerprint.setText(keystore.getKeyDerivation().getMasterFingerprint()); + } else { + keystore.setKeyDerivation(new KeyDerivation("","")); } - label.textProperty().addListener((observable, oldValue, newValue) -> keystore.setLabel(newValue)); - fingerprint.textProperty().addListener((observable, oldValue, newValue) -> keystore.setKeyDerivation(new KeyDerivation(newValue, keystore.getKeyDerivation().getDerivationPath()))); - derivation.textProperty().addListener((observable, oldValue, newValue) -> keystore.setKeyDerivation(new KeyDerivation(keystore.getKeyDerivation().getMasterFingerprint(), newValue))); - xpub.textProperty().addListener((observable, oldValue, newValue) -> keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(newValue))); + label.textProperty().addListener((observable, oldValue, newValue) -> { + keystore.setLabel(newValue); + EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet())); + }); + fingerprint.textProperty().addListener((observable, oldValue, newValue) -> { + keystore.setKeyDerivation(new KeyDerivation(newValue, keystore.getKeyDerivation().getDerivationPath())); + EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet())); + }); + derivation.textProperty().addListener((observable, oldValue, newValue) -> { + if(KeyDerivation.isValid(newValue)) { + keystore.setKeyDerivation(new KeyDerivation(keystore.getKeyDerivation().getMasterFingerprint(), newValue)); + EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet())); + } + }); + xpub.textProperty().addListener((observable, oldValue, newValue) -> { + if(ExtendedPublicKey.isValid(newValue)) { + keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(newValue)); + EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet())); + } + }); } public TextField getLabel() { return label; } - private void setupValidation() { - validationSupport = new ValidationSupport(); + public ValidationSupport getValidationSupport() { + return validationSupport; + } + private void setupValidation() { validationSupport.registerValidator(label, Validator.combine( Validator.createEmptyValidator("Label is required"), (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Label is not unique", walletForm.getWallet().getKeystores().stream().filter(k -> k != keystore).map(Keystore::getLabel).collect(Collectors.toList()).contains(newValue)), diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java index a0f421b6..ef420e16 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java @@ -9,7 +9,9 @@ import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.CopyableLabel; +import com.sparrowwallet.sparrow.event.SettingsChangedEvent; import com.sparrowwallet.sparrow.event.WalletChangedEvent; +import javafx.application.Platform; import javafx.beans.property.SimpleIntegerProperty; import javafx.collections.FXCollections; import javafx.fxml.FXML; @@ -54,6 +56,12 @@ public class SettingsController extends WalletFormController implements Initiali private TabPane keystoreTabs; + @FXML + private Button apply; + + @FXML + private Button revert; + private final SimpleIntegerProperty totalKeystores = new SimpleIntegerProperty(0); @Override @@ -63,15 +71,17 @@ public class SettingsController extends WalletFormController implements Initiali @Override public void initializeView() { - Wallet wallet = walletForm.getWallet(); - keystoreTabs = new TabPane(); keystoreTabsPane.getChildren().add(Borders.wrap(keystoreTabs).etchedBorder().outerPadding(10, 5, 0 ,0).innerPadding(0).raised().buildAll()); policyType.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, policyType) -> { - wallet.setPolicyType(policyType); + walletForm.getWallet().setPolicyType(policyType); + scriptType.setItems(FXCollections.observableArrayList(ScriptType.getScriptTypesForPolicyType(policyType))); - scriptType.getSelectionModel().select(policyType.getDefaultScriptType()); + if(!ScriptType.getScriptTypesForPolicyType(policyType).contains(walletForm.getWallet().getScriptType())) { + scriptType.getSelectionModel().select(policyType.getDefaultScriptType()); + } + multisigFieldset.setVisible(policyType.equals(PolicyType.MULTI)); if(policyType.equals(PolicyType.MULTI)) { totalKeystores.bind(multisigControl.highValueProperty()); @@ -82,51 +92,74 @@ public class SettingsController extends WalletFormController implements Initiali }); scriptType.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, scriptType) -> { - int threshold = wallet.getPolicyType().equals(PolicyType.MULTI) ? (int)multisigControl.lowValueProperty().get() : 1; - wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), scriptType, wallet.getKeystores(), threshold)); - EventManager.get().post(new WalletChangedEvent(wallet)); + if(scriptType != null) { + walletForm.getWallet().setScriptType(scriptType); + } + + EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet())); }); multisigLowLabel.textProperty().bind(multisigControl.lowValueProperty().asString("%.0f") ); multisigHighLabel.textProperty().bind(multisigControl.highValueProperty().asString("%.0f")); multisigControl.lowValueProperty().addListener((observable, oldValue, threshold) -> { - wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), threshold.intValue())); - EventManager.get().post(new WalletChangedEvent(wallet)); + EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet())); }); multisigFieldset.managedProperty().bind(multisigFieldset.visibleProperty()); totalKeystores.addListener((observable, oldValue, numCosigners) -> { - int keystoreCount = wallet.getKeystores().size(); - int keystoreNameCount = keystoreCount; + int keystoreCount = walletForm.getWallet().getKeystores().size(); + int keystoreNameCount = keystoreCount + 1; while(keystoreCount < numCosigners.intValue()) { keystoreCount++; String name = "Keystore " + keystoreNameCount; - while(wallet.getKeystores().stream().map(Keystore::getLabel).collect(Collectors.toList()).contains(name)) { + while(walletForm.getWallet().getKeystores().stream().map(Keystore::getLabel).collect(Collectors.toList()).contains(name)) { name = "Keystore " + (++keystoreNameCount); } - wallet.getKeystores().add(new Keystore(name)); + walletForm.getWallet().getKeystores().add(new Keystore(name)); } - wallet.setKeystores(wallet.getKeystores().subList(0, numCosigners.intValue())); + walletForm.getWallet().setKeystores(walletForm.getWallet().getKeystores().subList(0, numCosigners.intValue())); - for(int i = 0; i < wallet.getKeystores().size(); i++) { - Keystore keystore = wallet.getKeystores().get(i); + for(int i = 0; i < walletForm.getWallet().getKeystores().size(); i++) { + Keystore keystore = walletForm.getWallet().getKeystores().get(i); if(keystoreTabs.getTabs().size() == i) { - Tab tab = getKeystoreTab(wallet, keystore); + Tab tab = getKeystoreTab(walletForm.getWallet(), keystore); keystoreTabs.getTabs().add(tab); } } - while(keystoreTabs.getTabs().size() > wallet.getKeystores().size()) { + while(keystoreTabs.getTabs().size() > walletForm.getWallet().getKeystores().size()) { keystoreTabs.getTabs().remove(keystoreTabs.getTabs().size() - 1); } - if(wallet.getPolicyType().equals(PolicyType.MULTI)) { - wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), wallet.getDefaultPolicy().getNumSignaturesRequired())); - EventManager.get().post(new WalletChangedEvent(wallet)); + if(walletForm.getWallet().getPolicyType().equals(PolicyType.MULTI)) { + EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet())); } }); + revert.setOnAction(event -> { + keystoreTabs.getTabs().removeAll(keystoreTabs.getTabs()); + totalKeystores.unbind(); + totalKeystores.setValue(0); + walletForm.revert(); + setFieldsFromWallet(walletForm.getWallet()); + }); + + apply.setOnAction(event -> { + try { + walletForm.save(); + revert.setDisable(true); + apply.setDisable(true); + EventManager.get().post(new WalletChangedEvent(walletForm.getWallet())); + } catch (IOException e) { + AppController.showErrorDialog("Error saving file", e.getMessage()); + } + }); + + setFieldsFromWallet(walletForm.getWallet()); + } + + private void setFieldsFromWallet(Wallet wallet) { if(wallet.getPolicyType() == null) { wallet.setPolicyType(PolicyType.SINGLE); wallet.setScriptType(ScriptType.P2WPKH); @@ -135,20 +168,23 @@ public class SettingsController extends WalletFormController implements Initiali } if(wallet.getPolicyType().equals(PolicyType.SINGLE)) { - totalKeystores.setValue(wallet.getKeystores().size()); + totalKeystores.setValue(1); } else if(wallet.getPolicyType().equals(PolicyType.MULTI)) { + multisigControl.lowValueProperty().set(wallet.getDefaultPolicy().getNumSignaturesRequired()); multisigControl.highValueProperty().set(wallet.getKeystores().size()); + totalKeystores.bind(multisigControl.highValueProperty()); } if(wallet.getPolicyType() != null) { policyType.getSelectionModel().select(walletForm.getWallet().getPolicyType()); - } else { - policyType.getSelectionModel().select(0); } if(wallet.getScriptType() != null) { scriptType.getSelectionModel().select(walletForm.getWallet().getScriptType()); } + + revert.setDisable(true); + apply.setDisable(true); } private Tab getKeystoreTab(Wallet wallet, Keystore keystore) { @@ -162,14 +198,47 @@ public class SettingsController extends WalletFormController implements Initiali controller.setKeystore(getWalletForm(), keystore); tab.textProperty().bind(controller.getLabel().textProperty()); + controller.getValidationSupport().validationResultProperty().addListener((o, oldValue, result) -> { + if(result.getErrors().isEmpty()) { + tab.getStyleClass().remove("tab-error"); + tab.setTooltip(null); + apply.setDisable(false); + } else { + if(!tab.getStyleClass().contains("tab-error")) { + tab.getStyleClass().add("tab-error"); + } + tab.setTooltip(new Tooltip(result.getErrors().iterator().next().getText())); + apply.setDisable(true); + } + }); + return tab; } catch (IOException e) { throw new RuntimeException(e); } } + private boolean tabsValidate() { + for(Tab tab : keystoreTabs.getTabs()) { + if(tab.getStyleClass().contains("tab-error")) { + return false; + } + } + + return true; + } + @Subscribe - public void updateMiniscript(WalletChangedEvent event) { + public void update(SettingsChangedEvent event) { + Wallet wallet = event.getWallet(); + if(wallet.getPolicyType() == PolicyType.SINGLE) { + wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), 1)); + } else if(wallet.getPolicyType() == PolicyType.MULTI) { + wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), (int)multisigControl.getLowValue())); + } + spendingMiniscript.setText(event.getWallet().getDefaultPolicy().getMiniscript().getScript()); + revert.setDisable(false); + Platform.runLater(() -> apply.setDisable(!tabsValidate())); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java index 8f13706b..cec7645e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java @@ -1,19 +1,32 @@ package com.sparrowwallet.sparrow.wallet; import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.sparrow.storage.Storage; + +import java.io.File; +import java.io.IOException; public class WalletForm { + private File walletFile; + private Wallet oldWallet; private Wallet wallet; - public WalletForm(Wallet wallet) { - this.wallet = wallet; + public WalletForm(File walletFile, Wallet currentWallet) { + this.walletFile = walletFile; + this.oldWallet = currentWallet; + this.wallet = currentWallet.copy(); } public Wallet getWallet() { return wallet; } - public void setWallet(Wallet wallet) { - this.wallet = wallet; + public void revert() { + this.wallet = oldWallet.copy(); + } + + public void save() throws IOException { + Storage.getStorage().storeWallet(walletFile, wallet); + oldWallet = wallet.copy(); } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 95d55dba..66d3384e 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -8,5 +8,6 @@ open module com.sparrowwallet.sparrow { requires com.sparrowwallet.drongo; requires com.google.common; requires flowless; + requires com.google.gson; requires javafx.swing; } \ No newline at end of file diff --git a/src/main/resources/com/sparrowwallet/sparrow/app.fxml b/src/main/resources/com/sparrowwallet/sparrow/app.fxml index 6f82cc19..4891faa4 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/app.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/app.fxml @@ -13,7 +13,8 @@ - + + diff --git a/src/main/resources/com/sparrowwallet/sparrow/general.css b/src/main/resources/com/sparrowwallet/sparrow/general.css index 3e03cad5..9ede2508 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/general.css +++ b/src/main/resources/com/sparrowwallet/sparrow/general.css @@ -34,8 +34,12 @@ -fx-translate-x: -20px; } +.tab-error > .tab-container { + -fx-effect: dropshadow(three-pass-box, rgba(202, 18, 67, .6), 7, 0, 0, 0); +} + .error { - -fx-effect: dropshadow(three-pass-box, darkred, 7, 0, 0, 0); + -fx-effect: dropshadow(three-pass-box, rgb(202, 18, 67), 7, 0, 0, 0); } .warning { diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml index b7f1fb7b..0b90d191 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml @@ -16,7 +16,7 @@
- + diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml index 956061d8..b22e24ef 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/settings.fxml @@ -10,74 +10,87 @@ - - - - - - - - - - - - + +
+ + + + + + + + + + + + - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
-
-
- - - - - - - - -
-
+
+
+ + + + + + + + +
+
-
-
- - - -
-
+
+
+ + + +
+
-
-
- -
-
-
+
+
+ +
+
+ +
+ + + + + +