From e6eea67c4b1808fdab0cbe8c4384a1c26716536f Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Tue, 7 Nov 2023 16:38:33 +0200 Subject: [PATCH] add search all open wallets functionality, include matches on transaction output addresses --- .../sparrowwallet/sparrow/AppController.java | 38 ++++++++++--- .../sparrow/control/SearchWalletDialog.java | 54 +++++++++++++------ .../com/sparrowwallet/sparrow/app.fxml | 4 +- 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index e4aa0189..17f3badd 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -1516,19 +1516,41 @@ public class AppController implements Initializable { TabPane subTabs = (TabPane) selectedTab.getContent(); List walletForms = subTabs.getTabs().stream().map(subTab -> ((WalletTabData)subTab.getUserData()).getWalletForm()).collect(Collectors.toList()); if(!walletForms.isEmpty()) { - SearchWalletDialog searchWalletDialog = new SearchWalletDialog(walletForms); - searchWalletDialog.initOwner(rootStack.getScene().getWindow()); - Optional optEntry = searchWalletDialog.showAndWait(); - if(optEntry.isPresent()) { - Entry entry = optEntry.get(); - EventManager.get().post(new FunctionActionEvent(entry.getWalletFunction(), entry.getWallet())); - Platform.runLater(() -> EventManager.get().post(new SelectEntryEvent(entry))); - } + searchWallets(walletForms); } } } } + public void searchAllWallets(ActionEvent event) { + List allWalletForms = new ArrayList<>(); + for(Tab tab : tabs.getTabs()) { + TabData tabData = (TabData)tab.getUserData(); + if(tabData instanceof WalletTabData) { + TabPane subTabs = (TabPane)tab.getContent(); + allWalletForms.addAll(subTabs.getTabs().stream().map(subTab -> ((WalletTabData)subTab.getUserData()).getWalletForm()) + .filter(walletForm -> walletForm.getWallet().isValid() && !walletForm.isLocked()).collect(Collectors.toList())); + } + } + + if(allWalletForms.isEmpty()) { + showErrorDialog("No wallets", "There are no open and unlocked wallets to search."); + } else { + searchWallets(allWalletForms); + } + } + + private void searchWallets(List walletForms) { + SearchWalletDialog searchWalletDialog = new SearchWalletDialog(walletForms); + searchWalletDialog.initOwner(rootStack.getScene().getWindow()); + Optional optEntry = searchWalletDialog.showAndWait(); + if(optEntry.isPresent()) { + Entry entry = optEntry.get(); + EventManager.get().post(new FunctionActionEvent(entry.getWalletFunction(), entry.getWallet())); + Platform.runLater(() -> EventManager.get().post(new SelectEntryEvent(entry))); + } + } + public void showWalletSummary(ActionEvent event) { Tab selectedTab = tabs.getSelectionModel().getSelectedItem(); if(selectedTab != null) { diff --git a/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java index ef2ebd32..f2d11257 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java @@ -1,6 +1,8 @@ package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.KeyPurpose; +import com.sparrowwallet.drongo.address.Address; +import com.sparrowwallet.drongo.address.InvalidAddressException; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.wallet.*; @@ -21,6 +23,7 @@ import tornadofx.control.Form; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Objects; public class SearchWalletDialog extends Dialog { private static final Logger log = LoggerFactory.getLogger(SearchWalletDialog.class); @@ -36,13 +39,16 @@ public class SearchWalletDialog extends Dialog { throw new IllegalArgumentException("No wallets selected to search"); } + boolean showWallet = walletForms.stream().map(WalletForm::getMasterWallet).distinct().limit(2).count() > 1; + boolean showAccount = walletForms.stream().anyMatch(walletForm -> !walletForm.getWallet().isMasterWallet() || !walletForm.getNestedWalletForms().isEmpty()); + final DialogPane dialogPane = getDialogPane(); dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm()); dialogPane.getStylesheets().add(AppServices.class.getResource("dialog.css").toExternalForm()); dialogPane.getStylesheets().add(AppServices.class.getResource("wallet/wallet.css").toExternalForm()); dialogPane.getStylesheets().add(AppServices.class.getResource("search.css").toExternalForm()); AppServices.setStageIcon(dialogPane.getScene().getWindow()); - dialogPane.setHeaderText("Search Wallet"); + dialogPane.setHeaderText(showWallet ? "Search All Wallets" : "Search Wallet"); Image image = new Image("image/sparrow-small.png", 50, 50, false, false); if(!image.isError()) { @@ -69,11 +75,9 @@ public class SearchWalletDialog extends Dialog { fieldset.getChildren().addAll(searchField); form.getChildren().add(fieldset); - boolean showWallet = walletForms.size() > 1 || walletForms.stream().anyMatch(walletForm -> !walletForm.getNestedWalletForms().isEmpty()); - results = new CoinTreeTable(); results.setShowRoot(false); - results.setPrefWidth(showWallet ? 950 : 850); + results.setPrefWidth(showWallet || showAccount ? 950 : 850); results.setUnitFormat(walletForms.iterator().next().getWallet()); results.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY); results.setPlaceholder(new Label("No results")); @@ -81,11 +85,19 @@ public class SearchWalletDialog extends Dialog { if(showWallet) { TreeTableColumn walletColumn = new TreeTableColumn<>("Wallet"); walletColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { - return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getWallet().getDisplayName()); + return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getWallet().getMasterName()); }); results.getColumns().add(walletColumn); } + if(showAccount) { + TreeTableColumn accountColumn = new TreeTableColumn<>("Account"); + accountColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { + return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getWallet().getDisplayName()); + }); + results.getColumns().add(accountColumn); + } + TreeTableColumn typeColumn = new TreeTableColumn<>("Type"); typeColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getEntryType()); @@ -135,7 +147,7 @@ public class SearchWalletDialog extends Dialog { }); search.textProperty().addListener((observable, oldValue, newValue) -> { - searchWallet(newValue.toLowerCase(Locale.ROOT)); + searchWallets(newValue.toLowerCase(Locale.ROOT)); }); setResizable(true); @@ -145,17 +157,12 @@ public class SearchWalletDialog extends Dialog { Platform.runLater(search::requestFocus); } - private void searchWallet(String searchText) { + private void searchWallets(String searchText) { List matchingEntries = new ArrayList<>(); if(!searchText.isEmpty()) { - Long searchValue = null; - - try { - searchValue = Math.abs(Long.parseLong(searchText)); - } catch(NumberFormatException e) { - //ignore - } + Long searchValue = getSearchValue(searchText); + Address searchAddress = getSearchAddress(searchText); for(WalletForm walletForm : walletForms) { WalletTransactionsEntry walletTransactionsEntry = walletForm.getWalletTransactionsEntry(); @@ -163,7 +170,8 @@ public class SearchWalletDialog extends Dialog { if(entry instanceof TransactionEntry transactionEntry) { if(transactionEntry.getBlockTransaction().getHash().toString().equals(searchText) || (transactionEntry.getLabel() != null && transactionEntry.getLabel().toLowerCase(Locale.ROOT).contains(searchText)) || - (transactionEntry.getValue() != null && searchValue != null && Math.abs(transactionEntry.getValue()) == searchValue)) { + (transactionEntry.getValue() != null && searchValue != null && Math.abs(transactionEntry.getValue()) == searchValue) || + (searchAddress != null && transactionEntry.getBlockTransaction().getTransaction().getOutputs().stream().map(output -> output.getScript().getToAddress()).filter(Objects::nonNull).anyMatch(address -> address.equals(searchAddress)))) { matchingEntries.add(entry); } } @@ -215,6 +223,22 @@ public class SearchWalletDialog extends Dialog { results.setRoot(rootItem); } + private Long getSearchValue(String searchText) { + try { + return Math.abs(Long.parseLong(searchText)); + } catch(NumberFormatException e) { + return null; + } + } + + private Address getSearchAddress(String searchText) { + try { + return Address.fromString(searchText); + } catch(InvalidAddressException e) { + return null; + } + } + private static class SearchWalletEntry extends Entry { public SearchWalletEntry(Wallet wallet, List entries) { super(wallet, wallet.getName(), entries); diff --git a/src/main/resources/com/sparrowwallet/sparrow/app.fxml b/src/main/resources/com/sparrowwallet/sparrow/app.fxml index aa5c1181..a66c2245 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/app.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/app.fxml @@ -124,8 +124,10 @@ - + + +