From 7bb22419df0efb87fcb1aef8ebe0ea66f94a8b82 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Tue, 18 Jul 2023 10:09:37 +0200 Subject: [PATCH] add rename wallet menu command --- .../sparrowwallet/sparrow/AppController.java | 45 ++++++++++++++++--- .../sparrow/control/WalletNameDialog.java | 12 +++-- .../com/sparrowwallet/sparrow/io/Sparrow.java | 6 ++- .../com/sparrowwallet/sparrow/io/Storage.java | 37 +++++++++++++-- .../sparrow/io/db/DbPersistence.java | 2 +- .../sparrow/io/db/MixConfigDao.java | 1 + .../com/sparrowwallet/sparrow/app.fxml | 1 + 7 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 0d6617c0..87102f15 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -220,6 +220,8 @@ public class AppController implements Initializable { private final Set emptyLoadingWallets = new LinkedHashSet<>(); + private final Map renamedWallets = new HashMap<>(); + private final ChangeListener serverToggleOnlineListener = (observable, oldValue, newValue) -> { Platform.runLater(() -> setServerToggleTooltip(getCurrentBlockHeight())); }; @@ -851,6 +853,10 @@ public class AppController implements Initializable { } } + public void renameWallet(ActionEvent event) { + renameWallet(getSelectedWalletForm()); + } + public void deleteWallet(ActionEvent event) { deleteWallet(getSelectedWalletForm()); } @@ -1548,6 +1554,11 @@ public class AppController implements Initializable { tabs.getTabs().add(tab); tabs.getSelectionModel().select(tab); + + File oldWalletFile = renamedWallets.remove(storage.getWalletFile()); + if(oldWalletFile != null) { + deleteStorage(new Storage(oldWalletFile), false); + } } else { for(Tab walletTab : tabs.getTabs()) { TabData tabData = (TabData)walletTab.getUserData(); @@ -1970,6 +1981,30 @@ public class AppController implements Initializable { } } + private void renameWallet(WalletForm selectedWalletForm) { + WalletNameDialog walletNameDialog = new WalletNameDialog(selectedWalletForm.getMasterWallet().getName(), false, null, true); + Optional optName = walletNameDialog.showAndWait(); + if(optName.isPresent()) { + File walletFile = Storage.getWalletFile(optName.get().getName() + "." + PersistenceType.DB.getExtension()); + if(walletFile.exists()) { + showErrorDialog("Error renaming wallet", "Wallet file " + walletFile.getAbsolutePath() + " already exists."); + return; + } + + Storage.CopyWalletService copyWalletService = new Storage.CopyWalletService(selectedWalletForm.getWallet(), walletFile); + copyWalletService.setOnSucceeded(event -> { + renamedWallets.put(walletFile, selectedWalletForm.getStorage().getWalletFile()); + tabs.getTabs().remove(tabs.getSelectionModel().getSelectedItem()); + openWalletFile(walletFile, true); + }); + copyWalletService.setOnFailed(event -> { + log.error("Error renaming wallet", event.getSource().getException()); + showErrorDialog("Error renaming wallet", event.getSource().getException().getMessage()); + }); + copyWalletService.start(); + } + } + private void deleteWallet(WalletForm selectedWalletForm) { Optional optButtonType = AppServices.showWarningDialog("Delete " + selectedWalletForm.getWallet().getMasterName() + "?", "The wallet file and any backups will be deleted. Are you sure?", ButtonType.NO, ButtonType.YES); if(optButtonType.isPresent() && optButtonType.get() == ButtonType.YES) { @@ -1985,7 +2020,7 @@ public class AppController implements Initializable { try { tabs.getTabs().remove(tabs.getSelectionModel().getSelectedItem()); - deleteStorage(storage); + deleteStorage(storage, true); } finally { encryptionFullKey.clear(); password.get().clear(); @@ -2007,15 +2042,15 @@ public class AppController implements Initializable { } } else { tabs.getTabs().remove(tabs.getSelectionModel().getSelectedItem()); - deleteStorage(storage); + deleteStorage(storage, true); } } } - private void deleteStorage(Storage storage) { + private void deleteStorage(Storage storage, boolean deleteBackups) { if(storage.isClosed()) { Platform.runLater(() -> { - Storage.DeleteWalletService deleteWalletService = new Storage.DeleteWalletService(storage); + Storage.DeleteWalletService deleteWalletService = new Storage.DeleteWalletService(storage, deleteBackups); deleteWalletService.setDelay(Duration.seconds(3)); deleteWalletService.setPeriod(Duration.hours(1)); deleteWalletService.setOnSucceeded(event -> { @@ -2031,7 +2066,7 @@ public class AppController implements Initializable { deleteWalletService.start(); }); } else { - Platform.runLater(() -> deleteStorage(storage)); + Platform.runLater(() -> deleteStorage(storage, deleteBackups)); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletNameDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletNameDialog.java index af4d3d95..b9a58e51 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WalletNameDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WalletNameDialog.java @@ -42,9 +42,13 @@ public class WalletNameDialog extends Dialog } public WalletNameDialog(String initialName, boolean hasExistingTransactions, Date startDate) { + this(initialName, hasExistingTransactions, startDate, false); + } + + public WalletNameDialog(String initialName, boolean hasExistingTransactions, Date startDate, boolean rename) { final DialogPane dialogPane = getDialogPane(); AppServices.setStageIcon(dialogPane.getScene().getWindow()); - boolean requestBirthDate = (Config.get().getServerType() == null || Config.get().getServerType() == ServerType.BITCOIN_CORE); + boolean requestBirthDate = !rename && (Config.get().getServerType() == null || Config.get().getServerType() == ServerType.BITCOIN_CORE); setTitle("Wallet Name"); dialogPane.setHeaderText("Enter a name for this wallet:"); @@ -121,16 +125,16 @@ public class WalletNameDialog extends Dialog )); }); - final ButtonType okButtonType = new javafx.scene.control.ButtonType("Create Wallet", ButtonBar.ButtonData.OK_DONE); + final ButtonType okButtonType = new javafx.scene.control.ButtonType((rename ? "Rename" : "Create") + " Wallet", ButtonBar.ButtonData.OK_DONE); dialogPane.getButtonTypes().addAll(okButtonType); Button okButton = (Button) dialogPane.lookupButton(okButtonType); BooleanBinding isInvalid = Bindings.createBooleanBinding(() -> - name.getText().length() == 0 || Storage.walletExists(name.getText()) || (existingCheck.isSelected() && existingPicker.getValue() == null), name.textProperty(), existingCheck.selectedProperty(), existingPicker.valueProperty()); + name.getText().trim().length() == 0 || Storage.walletExists(name.getText()) || (existingCheck.isSelected() && existingPicker.getValue() == null), name.textProperty(), existingCheck.selectedProperty(), existingPicker.valueProperty()); okButton.disableProperty().bind(isInvalid); name.setPromptText("Wallet Name"); Platform.runLater(name::requestFocus); - setResultConverter(dialogButton -> dialogButton == okButtonType ? new NameAndBirthDate(name.getText(), existingPicker.getValue()) : null); + setResultConverter(dialogButton -> dialogButton == okButtonType ? new NameAndBirthDate(name.getText().trim(), existingPicker.getValue()) : null); } public static class NameAndBirthDate { diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java b/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java index 5aeb82fe..4eac7586 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Sparrow.java @@ -39,8 +39,10 @@ public class Sparrow implements WalletImport, WalletExport { Storage tempStorage = new Storage(persistence, tempFile); tempStorage.setKeyDeriver(storage.getKeyDeriver()); tempStorage.setEncryptionPubKey(storage.getEncryptionPubKey()); - tempStorage.saveWallet(exportedWallet); - for(Wallet childWallet : exportedWallet.getChildWallets()) { + + Wallet copy = exportedWallet.copy(); + tempStorage.saveWallet(copy); + for(Wallet childWallet : copy.getChildWallets()) { tempStorage.saveWallet(childWallet); } persistence.close(); diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java index 1abf8ca8..505acd59 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java @@ -265,8 +265,11 @@ public class Storage { persistence.copyWallet(walletFile, outputStream); } - public boolean delete() { - deleteBackups(); + public boolean delete(boolean deleteBackups) { + if(deleteBackups) { + deleteBackups(); + } + return IOUtils.secureDelete(walletFile); } @@ -735,18 +738,44 @@ public class Storage { } } + public static class CopyWalletService extends Service { + private final Wallet wallet; + private final File newWalletFile; + + public CopyWalletService(Wallet wallet, File newWalletFile) { + this.wallet = wallet; + this.newWalletFile = newWalletFile; + } + + @Override + protected Task createTask() { + return new Task<>() { + protected Void call() throws IOException, ExportException { + Sparrow export = new Sparrow(); + try(BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(newWalletFile))) { + export.exportWallet(wallet, outputStream); + } + + return null; + } + }; + } + } + public static class DeleteWalletService extends ScheduledService { private final Storage storage; + private final boolean deleteBackups; - public DeleteWalletService(Storage storage) { + public DeleteWalletService(Storage storage, boolean deleteBackups) { this.storage = storage; + this.deleteBackups = deleteBackups; } @Override protected Task createTask() { return new Task<>() { protected Boolean call() { - return storage.delete(); + return storage.delete(deleteBackups); } }; } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java index a7d74020..7c3da094 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java @@ -583,7 +583,7 @@ public class DbPersistence implements Persistence { @Override public boolean isClosed() { - return dataSource.isClosed(); + return dataSource == null || dataSource.isClosed(); } @Override diff --git a/src/main/java/com/sparrowwallet/sparrow/io/db/MixConfigDao.java b/src/main/java/com/sparrowwallet/sparrow/io/db/MixConfigDao.java index 773bb2d3..77c03ff5 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/db/MixConfigDao.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/db/MixConfigDao.java @@ -21,6 +21,7 @@ public interface MixConfigDao { default void addMixConfig(Wallet wallet) { if(wallet.getMixConfig() != null) { + wallet.getMixConfig().setId(null); addOrUpdate(wallet, wallet.getMixConfig()); } } diff --git a/src/main/resources/com/sparrowwallet/sparrow/app.fxml b/src/main/resources/com/sparrowwallet/sparrow/app.fxml index 9917d27c..2dcf3c24 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/app.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/app.fxml @@ -49,6 +49,7 @@ +