mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 13:16:44 +00:00
add search all open wallets functionality, include matches on transaction output addresses
This commit is contained in:
parent
ea3e0ca34a
commit
e6eea67c4b
3 changed files with 72 additions and 24 deletions
|
@ -1516,6 +1516,31 @@ public class AppController implements Initializable {
|
||||||
TabPane subTabs = (TabPane) selectedTab.getContent();
|
TabPane subTabs = (TabPane) selectedTab.getContent();
|
||||||
List<WalletForm> walletForms = subTabs.getTabs().stream().map(subTab -> ((WalletTabData)subTab.getUserData()).getWalletForm()).collect(Collectors.toList());
|
List<WalletForm> walletForms = subTabs.getTabs().stream().map(subTab -> ((WalletTabData)subTab.getUserData()).getWalletForm()).collect(Collectors.toList());
|
||||||
if(!walletForms.isEmpty()) {
|
if(!walletForms.isEmpty()) {
|
||||||
|
searchWallets(walletForms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void searchAllWallets(ActionEvent event) {
|
||||||
|
List<WalletForm> 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<WalletForm> walletForms) {
|
||||||
SearchWalletDialog searchWalletDialog = new SearchWalletDialog(walletForms);
|
SearchWalletDialog searchWalletDialog = new SearchWalletDialog(walletForms);
|
||||||
searchWalletDialog.initOwner(rootStack.getScene().getWindow());
|
searchWalletDialog.initOwner(rootStack.getScene().getWindow());
|
||||||
Optional<Entry> optEntry = searchWalletDialog.showAndWait();
|
Optional<Entry> optEntry = searchWalletDialog.showAndWait();
|
||||||
|
@ -1525,9 +1550,6 @@ public class AppController implements Initializable {
|
||||||
Platform.runLater(() -> EventManager.get().post(new SelectEntryEvent(entry)));
|
Platform.runLater(() -> EventManager.get().post(new SelectEntryEvent(entry)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showWalletSummary(ActionEvent event) {
|
public void showWalletSummary(ActionEvent event) {
|
||||||
Tab selectedTab = tabs.getSelectionModel().getSelectedItem();
|
Tab selectedTab = tabs.getSelectionModel().getSelectedItem();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.sparrowwallet.sparrow.control;
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.KeyPurpose;
|
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.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.wallet.*;
|
import com.sparrowwallet.sparrow.wallet.*;
|
||||||
|
@ -21,6 +23,7 @@ import tornadofx.control.Form;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class SearchWalletDialog extends Dialog<Entry> {
|
public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(SearchWalletDialog.class);
|
private static final Logger log = LoggerFactory.getLogger(SearchWalletDialog.class);
|
||||||
|
@ -36,13 +39,16 @@ public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
throw new IllegalArgumentException("No wallets selected to search");
|
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();
|
final DialogPane dialogPane = getDialogPane();
|
||||||
dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm());
|
dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm());
|
||||||
dialogPane.getStylesheets().add(AppServices.class.getResource("dialog.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("wallet/wallet.css").toExternalForm());
|
||||||
dialogPane.getStylesheets().add(AppServices.class.getResource("search.css").toExternalForm());
|
dialogPane.getStylesheets().add(AppServices.class.getResource("search.css").toExternalForm());
|
||||||
AppServices.setStageIcon(dialogPane.getScene().getWindow());
|
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);
|
Image image = new Image("image/sparrow-small.png", 50, 50, false, false);
|
||||||
if(!image.isError()) {
|
if(!image.isError()) {
|
||||||
|
@ -69,11 +75,9 @@ public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
fieldset.getChildren().addAll(searchField);
|
fieldset.getChildren().addAll(searchField);
|
||||||
form.getChildren().add(fieldset);
|
form.getChildren().add(fieldset);
|
||||||
|
|
||||||
boolean showWallet = walletForms.size() > 1 || walletForms.stream().anyMatch(walletForm -> !walletForm.getNestedWalletForms().isEmpty());
|
|
||||||
|
|
||||||
results = new CoinTreeTable();
|
results = new CoinTreeTable();
|
||||||
results.setShowRoot(false);
|
results.setShowRoot(false);
|
||||||
results.setPrefWidth(showWallet ? 950 : 850);
|
results.setPrefWidth(showWallet || showAccount ? 950 : 850);
|
||||||
results.setUnitFormat(walletForms.iterator().next().getWallet());
|
results.setUnitFormat(walletForms.iterator().next().getWallet());
|
||||||
results.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
|
results.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
results.setPlaceholder(new Label("No results"));
|
results.setPlaceholder(new Label("No results"));
|
||||||
|
@ -81,11 +85,19 @@ public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
if(showWallet) {
|
if(showWallet) {
|
||||||
TreeTableColumn<Entry, String> walletColumn = new TreeTableColumn<>("Wallet");
|
TreeTableColumn<Entry, String> walletColumn = new TreeTableColumn<>("Wallet");
|
||||||
walletColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, String> param) -> {
|
walletColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, String> param) -> {
|
||||||
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getWallet().getDisplayName());
|
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getWallet().getMasterName());
|
||||||
});
|
});
|
||||||
results.getColumns().add(walletColumn);
|
results.getColumns().add(walletColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(showAccount) {
|
||||||
|
TreeTableColumn<Entry, String> accountColumn = new TreeTableColumn<>("Account");
|
||||||
|
accountColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, String> param) -> {
|
||||||
|
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getWallet().getDisplayName());
|
||||||
|
});
|
||||||
|
results.getColumns().add(accountColumn);
|
||||||
|
}
|
||||||
|
|
||||||
TreeTableColumn<Entry, String> typeColumn = new TreeTableColumn<>("Type");
|
TreeTableColumn<Entry, String> typeColumn = new TreeTableColumn<>("Type");
|
||||||
typeColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, String> param) -> {
|
typeColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, String> param) -> {
|
||||||
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getEntryType());
|
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getEntryType());
|
||||||
|
@ -135,7 +147,7 @@ public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
});
|
});
|
||||||
|
|
||||||
search.textProperty().addListener((observable, oldValue, newValue) -> {
|
search.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
searchWallet(newValue.toLowerCase(Locale.ROOT));
|
searchWallets(newValue.toLowerCase(Locale.ROOT));
|
||||||
});
|
});
|
||||||
|
|
||||||
setResizable(true);
|
setResizable(true);
|
||||||
|
@ -145,17 +157,12 @@ public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
Platform.runLater(search::requestFocus);
|
Platform.runLater(search::requestFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void searchWallet(String searchText) {
|
private void searchWallets(String searchText) {
|
||||||
List<Entry> matchingEntries = new ArrayList<>();
|
List<Entry> matchingEntries = new ArrayList<>();
|
||||||
|
|
||||||
if(!searchText.isEmpty()) {
|
if(!searchText.isEmpty()) {
|
||||||
Long searchValue = null;
|
Long searchValue = getSearchValue(searchText);
|
||||||
|
Address searchAddress = getSearchAddress(searchText);
|
||||||
try {
|
|
||||||
searchValue = Math.abs(Long.parseLong(searchText));
|
|
||||||
} catch(NumberFormatException e) {
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
for(WalletForm walletForm : walletForms) {
|
for(WalletForm walletForm : walletForms) {
|
||||||
WalletTransactionsEntry walletTransactionsEntry = walletForm.getWalletTransactionsEntry();
|
WalletTransactionsEntry walletTransactionsEntry = walletForm.getWalletTransactionsEntry();
|
||||||
|
@ -163,7 +170,8 @@ public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
if(entry instanceof TransactionEntry transactionEntry) {
|
if(entry instanceof TransactionEntry transactionEntry) {
|
||||||
if(transactionEntry.getBlockTransaction().getHash().toString().equals(searchText) ||
|
if(transactionEntry.getBlockTransaction().getHash().toString().equals(searchText) ||
|
||||||
(transactionEntry.getLabel() != null && transactionEntry.getLabel().toLowerCase(Locale.ROOT).contains(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);
|
matchingEntries.add(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,6 +223,22 @@ public class SearchWalletDialog extends Dialog<Entry> {
|
||||||
results.setRoot(rootItem);
|
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 {
|
private static class SearchWalletEntry extends Entry {
|
||||||
public SearchWalletEntry(Wallet wallet, List<Entry> entries) {
|
public SearchWalletEntry(Wallet wallet, List<Entry> entries) {
|
||||||
super(wallet, wallet.getName(), entries);
|
super(wallet, wallet.getName(), entries);
|
||||||
|
|
|
@ -124,8 +124,10 @@
|
||||||
<MenuItem fx:id="lockWallet" mnemonicParsing="false" text="Lock Wallet" accelerator="Shortcut+L" onAction="#lockWallet"/>
|
<MenuItem fx:id="lockWallet" mnemonicParsing="false" text="Lock Wallet" accelerator="Shortcut+L" onAction="#lockWallet"/>
|
||||||
<MenuItem fx:id="lockAllWallets" mnemonicParsing="false" text="Lock All Wallets" accelerator="Shortcut+Shift+L" onAction="#lockWallets"/>
|
<MenuItem fx:id="lockAllWallets" mnemonicParsing="false" text="Lock All Wallets" accelerator="Shortcut+Shift+L" onAction="#lockWallets"/>
|
||||||
<SeparatorMenuItem />
|
<SeparatorMenuItem />
|
||||||
<MenuItem fx:id="showWalletSummary" mnemonicParsing="false" text="Show Wallet Summary" onAction="#showWalletSummary"/>
|
|
||||||
<MenuItem fx:id="searchWallet" mnemonicParsing="false" text="Search Wallet" accelerator="Shortcut+Shift+S" onAction="#searchWallet"/>
|
<MenuItem fx:id="searchWallet" mnemonicParsing="false" text="Search Wallet" accelerator="Shortcut+Shift+S" onAction="#searchWallet"/>
|
||||||
|
<MenuItem fx:id="searchAllWallets" mnemonicParsing="false" text="Search All Wallets" accelerator="Shortcut+Shift+Alt+S" onAction="#searchAllWallets"/>
|
||||||
|
<SeparatorMenuItem />
|
||||||
|
<MenuItem fx:id="showWalletSummary" mnemonicParsing="false" text="Show Wallet Summary" onAction="#showWalletSummary"/>
|
||||||
<MenuItem fx:id="refreshWallet" mnemonicParsing="false" text="Refresh Wallet" accelerator="Shortcut+R" onAction="#refreshWallet"/>
|
<MenuItem fx:id="refreshWallet" mnemonicParsing="false" text="Refresh Wallet" accelerator="Shortcut+R" onAction="#refreshWallet"/>
|
||||||
</items>
|
</items>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
Loading…
Reference in a new issue