add rename wallet menu command

This commit is contained in:
Craig Raw 2023-07-18 10:09:37 +02:00
parent c443bc78d3
commit 7bb22419df
7 changed files with 88 additions and 16 deletions

View file

@ -220,6 +220,8 @@ public class AppController implements Initializable {
private final Set<Wallet> emptyLoadingWallets = new LinkedHashSet<>(); private final Set<Wallet> emptyLoadingWallets = new LinkedHashSet<>();
private final Map<File, File> renamedWallets = new HashMap<>();
private final ChangeListener<Boolean> serverToggleOnlineListener = (observable, oldValue, newValue) -> { private final ChangeListener<Boolean> serverToggleOnlineListener = (observable, oldValue, newValue) -> {
Platform.runLater(() -> setServerToggleTooltip(getCurrentBlockHeight())); 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) { public void deleteWallet(ActionEvent event) {
deleteWallet(getSelectedWalletForm()); deleteWallet(getSelectedWalletForm());
} }
@ -1548,6 +1554,11 @@ public class AppController implements Initializable {
tabs.getTabs().add(tab); tabs.getTabs().add(tab);
tabs.getSelectionModel().select(tab); tabs.getSelectionModel().select(tab);
File oldWalletFile = renamedWallets.remove(storage.getWalletFile());
if(oldWalletFile != null) {
deleteStorage(new Storage(oldWalletFile), false);
}
} else { } else {
for(Tab walletTab : tabs.getTabs()) { for(Tab walletTab : tabs.getTabs()) {
TabData tabData = (TabData)walletTab.getUserData(); 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<WalletNameDialog.NameAndBirthDate> 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) { private void deleteWallet(WalletForm selectedWalletForm) {
Optional<ButtonType> optButtonType = AppServices.showWarningDialog("Delete " + selectedWalletForm.getWallet().getMasterName() + "?", "The wallet file and any backups will be deleted. Are you sure?", ButtonType.NO, ButtonType.YES); Optional<ButtonType> 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) { if(optButtonType.isPresent() && optButtonType.get() == ButtonType.YES) {
@ -1985,7 +2020,7 @@ public class AppController implements Initializable {
try { try {
tabs.getTabs().remove(tabs.getSelectionModel().getSelectedItem()); tabs.getTabs().remove(tabs.getSelectionModel().getSelectedItem());
deleteStorage(storage); deleteStorage(storage, true);
} finally { } finally {
encryptionFullKey.clear(); encryptionFullKey.clear();
password.get().clear(); password.get().clear();
@ -2007,15 +2042,15 @@ public class AppController implements Initializable {
} }
} else { } else {
tabs.getTabs().remove(tabs.getSelectionModel().getSelectedItem()); 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()) { if(storage.isClosed()) {
Platform.runLater(() -> { Platform.runLater(() -> {
Storage.DeleteWalletService deleteWalletService = new Storage.DeleteWalletService(storage); Storage.DeleteWalletService deleteWalletService = new Storage.DeleteWalletService(storage, deleteBackups);
deleteWalletService.setDelay(Duration.seconds(3)); deleteWalletService.setDelay(Duration.seconds(3));
deleteWalletService.setPeriod(Duration.hours(1)); deleteWalletService.setPeriod(Duration.hours(1));
deleteWalletService.setOnSucceeded(event -> { deleteWalletService.setOnSucceeded(event -> {
@ -2031,7 +2066,7 @@ public class AppController implements Initializable {
deleteWalletService.start(); deleteWalletService.start();
}); });
} else { } else {
Platform.runLater(() -> deleteStorage(storage)); Platform.runLater(() -> deleteStorage(storage, deleteBackups));
} }
} }

View file

@ -42,9 +42,13 @@ public class WalletNameDialog extends Dialog<WalletNameDialog.NameAndBirthDate>
} }
public WalletNameDialog(String initialName, boolean hasExistingTransactions, Date startDate) { 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(); final DialogPane dialogPane = getDialogPane();
AppServices.setStageIcon(dialogPane.getScene().getWindow()); 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"); setTitle("Wallet Name");
dialogPane.setHeaderText("Enter a name for this wallet:"); dialogPane.setHeaderText("Enter a name for this wallet:");
@ -121,16 +125,16 @@ public class WalletNameDialog extends Dialog<WalletNameDialog.NameAndBirthDate>
)); ));
}); });
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); dialogPane.getButtonTypes().addAll(okButtonType);
Button okButton = (Button) dialogPane.lookupButton(okButtonType); Button okButton = (Button) dialogPane.lookupButton(okButtonType);
BooleanBinding isInvalid = Bindings.createBooleanBinding(() -> 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); okButton.disableProperty().bind(isInvalid);
name.setPromptText("Wallet Name"); name.setPromptText("Wallet Name");
Platform.runLater(name::requestFocus); 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 { public static class NameAndBirthDate {

View file

@ -39,8 +39,10 @@ public class Sparrow implements WalletImport, WalletExport {
Storage tempStorage = new Storage(persistence, tempFile); Storage tempStorage = new Storage(persistence, tempFile);
tempStorage.setKeyDeriver(storage.getKeyDeriver()); tempStorage.setKeyDeriver(storage.getKeyDeriver());
tempStorage.setEncryptionPubKey(storage.getEncryptionPubKey()); 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); tempStorage.saveWallet(childWallet);
} }
persistence.close(); persistence.close();

View file

@ -265,8 +265,11 @@ public class Storage {
persistence.copyWallet(walletFile, outputStream); persistence.copyWallet(walletFile, outputStream);
} }
public boolean delete() { public boolean delete(boolean deleteBackups) {
if(deleteBackups) {
deleteBackups(); deleteBackups();
}
return IOUtils.secureDelete(walletFile); return IOUtils.secureDelete(walletFile);
} }
@ -735,18 +738,44 @@ public class Storage {
} }
} }
public static class CopyWalletService extends Service<Void> {
private final Wallet wallet;
private final File newWalletFile;
public CopyWalletService(Wallet wallet, File newWalletFile) {
this.wallet = wallet;
this.newWalletFile = newWalletFile;
}
@Override
protected Task<Void> 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<Boolean> { public static class DeleteWalletService extends ScheduledService<Boolean> {
private final Storage storage; private final Storage storage;
private final boolean deleteBackups;
public DeleteWalletService(Storage storage) { public DeleteWalletService(Storage storage, boolean deleteBackups) {
this.storage = storage; this.storage = storage;
this.deleteBackups = deleteBackups;
} }
@Override @Override
protected Task<Boolean> createTask() { protected Task<Boolean> createTask() {
return new Task<>() { return new Task<>() {
protected Boolean call() { protected Boolean call() {
return storage.delete(); return storage.delete(deleteBackups);
} }
}; };
} }

View file

@ -583,7 +583,7 @@ public class DbPersistence implements Persistence {
@Override @Override
public boolean isClosed() { public boolean isClosed() {
return dataSource.isClosed(); return dataSource == null || dataSource.isClosed();
} }
@Override @Override

View file

@ -21,6 +21,7 @@ public interface MixConfigDao {
default void addMixConfig(Wallet wallet) { default void addMixConfig(Wallet wallet) {
if(wallet.getMixConfig() != null) { if(wallet.getMixConfig() != null) {
wallet.getMixConfig().setId(null);
addOrUpdate(wallet, wallet.getMixConfig()); addOrUpdate(wallet, wallet.getMixConfig());
} }
} }

View file

@ -49,6 +49,7 @@
<SeparatorMenuItem styleClass="osxHide" /> <SeparatorMenuItem styleClass="osxHide" />
<MenuItem styleClass="osxHide" mnemonicParsing="false" text="Preferences..." accelerator="Shortcut+P" onAction="#openPreferences"/> <MenuItem styleClass="osxHide" mnemonicParsing="false" text="Preferences..." accelerator="Shortcut+P" onAction="#openPreferences"/>
<SeparatorMenuItem /> <SeparatorMenuItem />
<MenuItem fx:id="renameWallet" mnemonicParsing="false" text="Rename Wallet..." onAction="#renameWallet"/>
<MenuItem fx:id="deleteWallet" mnemonicParsing="false" text="Delete Wallet..." onAction="#deleteWallet"/> <MenuItem fx:id="deleteWallet" mnemonicParsing="false" text="Delete Wallet..." onAction="#deleteWallet"/>
<MenuItem fx:id="closeTab" mnemonicParsing="false" text="Close Tab" accelerator="Shortcut+W" onAction="#closeTab"/> <MenuItem fx:id="closeTab" mnemonicParsing="false" text="Close Tab" accelerator="Shortcut+W" onAction="#closeTab"/>
<MenuItem styleClass="osxHide" mnemonicParsing="false" text="Quit" accelerator="Shortcut+Q" onAction="#quit"/> <MenuItem styleClass="osxHide" mnemonicParsing="false" text="Quit" accelerator="Shortcut+Q" onAction="#quit"/>