subscription handling for multiple app windows

This commit is contained in:
Craig Raw 2020-12-07 14:12:11 +02:00
parent 722fd84ec1
commit dfa781e16c
15 changed files with 145 additions and 66 deletions

View file

@ -168,7 +168,7 @@ public class AppController implements Initializable {
boolean walletAdded = c.getAddedSubList().stream().anyMatch(tab -> ((TabData)tab.getUserData()).getType() == TabData.TabType.WALLET);
boolean walletRemoved = c.getRemoved().stream().anyMatch(tab -> ((TabData)tab.getUserData()).getType() == TabData.TabType.WALLET);
if(walletAdded || walletRemoved) {
EventManager.get().post(new OpenWalletsEvent(getOpenWallets()));
EventManager.get().post(new OpenWalletsEvent(tabs.getScene().getWindow(), getOpenWallets()));
}
List<WalletTabData> closedWalletTabs = c.getRemoved().stream().map(tab -> (TabData)tab.getUserData())
@ -361,7 +361,7 @@ public class AppController implements Initializable {
showErrorDialog("Invalid transaction ID", "A transaction with that ID could not be found.");
} else {
Platform.runLater(() -> {
EventManager.get().post(new ViewTransactionEvent(blockTransaction));
EventManager.get().post(new ViewTransactionEvent(tabs.getScene().getWindow(), blockTransaction));
});
}
});
@ -1000,27 +1000,28 @@ public class AppController implements Initializable {
@Subscribe
public void tabSelected(TabSelectedEvent event) {
//TODO: Handle multiple windows
String tabName = event.getTabName();
if(tabs.getScene() != null) {
Stage tabStage = (Stage)tabs.getScene().getWindow();
tabStage.setTitle("Sparrow - " + tabName);
}
if(tabs.getTabs().contains(event.getTab())) {
String tabName = event.getTabName();
if(tabs.getScene() != null) {
Stage tabStage = (Stage)tabs.getScene().getWindow();
tabStage.setTitle("Sparrow - " + tabName);
}
if(event instanceof TransactionTabSelectedEvent) {
TransactionTabSelectedEvent txTabEvent = (TransactionTabSelectedEvent)event;
TransactionTabData transactionTabData = txTabEvent.getTransactionTabData();
saveTransaction.setDisable(false);
saveTransaction.setText("Save " + (transactionTabData.getPsbt() == null || transactionTabData.getPsbt().getTransaction() != transactionTabData.getTransaction() ? "Transaction..." : "PSBT..."));
exportWallet.setDisable(true);
showTxHex.setDisable(false);
} else if(event instanceof WalletTabSelectedEvent) {
WalletTabSelectedEvent walletTabEvent = (WalletTabSelectedEvent)event;
WalletTabData walletTabData = walletTabEvent.getWalletTabData();
saveTransaction.setDisable(true);
saveTransaction.setText("Save Transaction...");
exportWallet.setDisable(walletTabData.getWallet() == null || !walletTabData.getWallet().isValid());
showTxHex.setDisable(true);
if(event instanceof TransactionTabSelectedEvent) {
TransactionTabSelectedEvent txTabEvent = (TransactionTabSelectedEvent)event;
TransactionTabData transactionTabData = txTabEvent.getTransactionTabData();
saveTransaction.setDisable(false);
saveTransaction.setText("Save " + (transactionTabData.getPsbt() == null || transactionTabData.getPsbt().getTransaction() != transactionTabData.getTransaction() ? "Transaction..." : "PSBT..."));
exportWallet.setDisable(true);
showTxHex.setDisable(false);
} else if(event instanceof WalletTabSelectedEvent) {
WalletTabSelectedEvent walletTabEvent = (WalletTabSelectedEvent)event;
WalletTabData walletTabData = walletTabEvent.getWalletTabData();
saveTransaction.setDisable(true);
saveTransaction.setText("Save Transaction...");
exportWallet.setDisable(walletTabData.getWallet() == null || !walletTabData.getWallet().isValid());
showTxHex.setDisable(true);
}
}
}
@ -1039,14 +1040,19 @@ public class AppController implements Initializable {
@Subscribe
public void walletSettingsChanged(WalletSettingsChangedEvent event) {
//TODO: Handle multiple windows
exportWallet.setDisable(!event.getWallet().isValid());
Tab tab = tabs.getSelectionModel().getSelectedItem();
TabData tabData = (TabData)tab.getUserData();
if(tabData instanceof WalletTabData) {
WalletTabData walletTabData = (WalletTabData)tabData;
if(walletTabData.getWalletForm().getWalletFile().equals(event.getWalletFile())) {
exportWallet.setDisable(!event.getWallet().isValid());
}
}
}
@Subscribe
public void newWalletTransactions(NewWalletTransactionsEvent event) {
//TODO: Handle multiple windows
if(Config.get().isNotifyNewTransactions()) {
if(Config.get().isNotifyNewTransactions() && getOpenWallets().containsKey(event.getWallet())) {
String text;
if(event.getBlockTransactions().size() == 1) {
BlockTransaction blockTransaction = event.getBlockTransactions().get(0);
@ -1184,16 +1190,18 @@ public class AppController implements Initializable {
@Subscribe
public void viewTransaction(ViewTransactionEvent event) {
//TODO: Handle multiple windows
Tab tab = addTransactionTab(event.getBlockTransaction(), event.getInitialView(), event.getInitialIndex());
tabs.getSelectionModel().select(tab);
if(tabs.getScene().getWindow().equals(event.getWindow())) {
Tab tab = addTransactionTab(event.getBlockTransaction(), event.getInitialView(), event.getInitialIndex());
tabs.getSelectionModel().select(tab);
}
}
@Subscribe
public void viewPSBT(ViewPSBTEvent event) {
//TODO: Handle multiple windows
Tab tab = addTransactionTab(event.getLabel(), event.getPsbt());
tabs.getSelectionModel().select(tab);
if(tabs.getScene().getWindow().equals(event.getWindow())) {
Tab tab = addTransactionTab(event.getLabel(), event.getPsbt());
tabs.getSelectionModel().select(tab);
}
}
@Subscribe
@ -1204,25 +1212,27 @@ public class AppController implements Initializable {
@Subscribe
public void requestOpenWallets(RequestOpenWalletsEvent event) {
//TODO: Handle multiple windows
EventManager.get().post(new OpenWalletsEvent(getOpenWallets()));
EventManager.get().post(new OpenWalletsEvent(tabs.getScene().getWindow(), getOpenWallets()));
}
@Subscribe
public void requestWalletOpen(RequestWalletOpenEvent event) {
//TODO: Handle multiple windows
openWallet(null);
if(tabs.getScene().getWindow().equals(event.getWindow())) {
openWallet(null);
}
}
@Subscribe
public void requestTransactionOpen(RequestTransactionOpenEvent event) {
//TODO: Handle multiple windows
openTransactionFromFile(null);
if(tabs.getScene().getWindow().equals(event.getWindow())) {
openTransactionFromFile(null);
}
}
@Subscribe
public void requestQRScan(RequestQRScanEvent event) {
//TODO: Handle multiple windows
openTransactionFromQR(null);
if(tabs.getScene().getWindow().equals(event.getWindow())) {
openTransactionFromQR(null);
}
}
}

View file

@ -52,6 +52,8 @@ public class AppServices {
private final MainApp application;
private final Map<Window, Map<Wallet, Storage>> windows = new LinkedHashMap<>();
private static final BooleanProperty onlineProperty = new SimpleBooleanProperty(false);
private ExchangeSource.RatesService ratesService;
@ -381,12 +383,14 @@ public class AppServices {
@Subscribe
public void openWallets(OpenWalletsEvent event) {
List<File> walletFiles = event.getWalletsMap().values().stream().map(Storage::getWalletFile).collect(Collectors.toList());
//TODO: Handle multiple windows
windows.put(event.getWindow(), event.getWalletsMap());
List<Map.Entry<Wallet, Storage>> allWallets = windows.values().stream().flatMap(map -> map.entrySet().stream()).collect(Collectors.toList());
List<File> walletFiles = allWallets.stream().map(entry -> entry.getValue().getWalletFile()).collect(Collectors.toList());
Config.get().setRecentWalletFiles(walletFiles);
boolean usbWallet = false;
for(Map.Entry<Wallet, Storage> entry : event.getWalletsMap().entrySet()) {
for(Map.Entry<Wallet, Storage> entry : allWallets) {
Wallet wallet = entry.getKey();
Storage storage = entry.getValue();

View file

@ -68,7 +68,7 @@ class EntryCell extends TreeTableCell<Entry, Entry> {
searchGlyph.setFontSize(12);
viewTransactionButton.setGraphic(searchGlyph);
viewTransactionButton.setOnAction(event -> {
EventManager.get().post(new ViewTransactionEvent(transactionEntry.getBlockTransaction()));
EventManager.get().post(new ViewTransactionEvent(this.getScene().getWindow(), transactionEntry.getBlockTransaction()));
});
actionBox.getChildren().add(viewTransactionButton);
@ -135,7 +135,7 @@ class EntryCell extends TreeTableCell<Entry, Entry> {
searchGlyph.setFontSize(12);
viewTransactionButton.setGraphic(searchGlyph);
viewTransactionButton.setOnAction(event -> {
EventManager.get().post(new ViewTransactionEvent(hashIndexEntry.getBlockTransaction(), hashIndexEntry));
EventManager.get().post(new ViewTransactionEvent(this.getScene().getWindow(), hashIndexEntry.getBlockTransaction(), hashIndexEntry));
});
actionBox.getChildren().add(viewTransactionButton);

View file

@ -304,7 +304,8 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
public void openWallets(OpenWalletsEvent event) {
Storage storage = event.getStorage(wallet);
if(storage == null) {
throw new IllegalStateException("Wallet " + wallet + " without Storage");
//Another window, ignore
return;
}
WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD);

View file

@ -2,18 +2,25 @@ package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.io.Storage;
import javafx.stage.Window;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class OpenWalletsEvent {
private final Window window;
private final Map<Wallet, Storage> walletsMap;
public OpenWalletsEvent(Map<Wallet, Storage> walletsMap) {
public OpenWalletsEvent(Window window, Map<Wallet, Storage> walletsMap) {
this.window = window;
this.walletsMap = walletsMap;
}
public Window getWindow() {
return window;
}
public List<Wallet> getWallets() {
return new ArrayList<>(walletsMap.keySet());
}

View file

@ -1,5 +1,18 @@
package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
/**
* Event class used to request the QRScanDialog is opened
*/
public class RequestQRScanEvent {
//Empty event class used to request the QRScanDialog is opened
private final Window window;
public RequestQRScanEvent(Window window) {
this.window = window;
}
public Window getWindow() {
return window;
}
}

View file

@ -1,5 +1,18 @@
package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
/**
* Event class used to request the transaction open file dialog
*/
public class RequestTransactionOpenEvent {
//Empty event class used to request the transaction open file dialog
private final Window window;
public RequestTransactionOpenEvent(Window window) {
this.window = window;
}
public Window getWindow() {
return window;
}
}

View file

@ -1,5 +1,18 @@
package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
/**
* Event class used to request the wallet open dialog
*/
public class RequestWalletOpenEvent {
//Empty event class used to request the wallet open dialog
private final Window window;
public RequestWalletOpenEvent(Window window) {
this.window = window;
}
public Window getWindow() {
return window;
}
}

View file

@ -2,24 +2,31 @@ package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.sparrow.transaction.TransactionView;
import javafx.stage.Window;
public class ViewPSBTEvent {
private final Window window;
private final String label;
private final PSBT psbt;
private final TransactionView initialView;
private final Integer initialIndex;
public ViewPSBTEvent(String label, PSBT psbt) {
this(label, psbt, TransactionView.HEADERS, null);
public ViewPSBTEvent(Window window, String label, PSBT psbt) {
this(window, label, psbt, TransactionView.HEADERS, null);
}
public ViewPSBTEvent(String label, PSBT psbt, TransactionView initialView, Integer initialIndex) {
public ViewPSBTEvent(Window window, String label, PSBT psbt, TransactionView initialView, Integer initialIndex) {
this.window = window;
this.label = label;
this.psbt = psbt;
this.initialView = initialView;
this.initialIndex = initialIndex;
}
public Window getWindow() {
return window;
}
public String getLabel() {
return label;
}

View file

@ -3,26 +3,33 @@ package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.sparrow.transaction.TransactionView;
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
import javafx.stage.Window;
public class ViewTransactionEvent {
private final Window window;
private final BlockTransaction blockTransaction;
private final TransactionView initialView;
private final Integer initialIndex;
public ViewTransactionEvent(BlockTransaction blockTransaction) {
this(blockTransaction, TransactionView.HEADERS, null);
public ViewTransactionEvent(Window window, BlockTransaction blockTransaction) {
this(window, blockTransaction, TransactionView.HEADERS, null);
}
public ViewTransactionEvent(BlockTransaction blockTransaction, HashIndexEntry hashIndexEntry) {
this(blockTransaction, hashIndexEntry.getType().equals(HashIndexEntry.Type.INPUT) ? TransactionView.INPUT : TransactionView.OUTPUT, (int)hashIndexEntry.getHashIndex().getIndex());
public ViewTransactionEvent(Window window, BlockTransaction blockTransaction, HashIndexEntry hashIndexEntry) {
this(window, blockTransaction, hashIndexEntry.getType().equals(HashIndexEntry.Type.INPUT) ? TransactionView.INPUT : TransactionView.OUTPUT, (int)hashIndexEntry.getHashIndex().getIndex());
}
public ViewTransactionEvent(BlockTransaction blockTransaction, TransactionView initialView, Integer initialIndex) {
public ViewTransactionEvent(Window window, BlockTransaction blockTransaction, TransactionView initialView, Integer initialIndex) {
this.window = window;
this.blockTransaction = blockTransaction;
this.initialView = initialView;
this.initialIndex = initialIndex;
}
public Window getWindow() {
return window;
}
public BlockTransaction getBlockTransaction() {
return blockTransaction;
}

View file

@ -609,7 +609,7 @@ public class HeadersController extends TransactionFormController implements Init
}
public void openWallet(ActionEvent event) {
EventManager.get().post(new RequestWalletOpenEvent());
EventManager.get().post(new RequestWalletOpenEvent(noWalletsWarningLink.getScene().getWindow()));
}
public void finalizeTransaction(ActionEvent event) {
@ -635,7 +635,7 @@ public class HeadersController extends TransactionFormController implements Init
ToggleButton toggleButton = (ToggleButton)event.getSource();
toggleButton.setSelected(false);
EventManager.get().post(new RequestQRScanEvent());
EventManager.get().post(new RequestQRScanEvent(toggleButton.getScene().getWindow()));
}
public void savePSBT(ActionEvent event) {
@ -668,7 +668,7 @@ public class HeadersController extends TransactionFormController implements Init
ToggleButton toggleButton = (ToggleButton)event.getSource();
toggleButton.setSelected(false);
EventManager.get().post(new RequestTransactionOpenEvent());
EventManager.get().post(new RequestTransactionOpenEvent(toggleButton.getScene().getWindow()));
}
public void signPSBT(ActionEvent event) {
@ -830,7 +830,7 @@ public class HeadersController extends TransactionFormController implements Init
try {
Payjoin payjoin = new Payjoin(payjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt());
PSBT proposalPsbt = payjoin.requestPayjoinPSBT(true);
EventManager.get().post(new ViewPSBTEvent(headersForm.getName() + " Payjoin", proposalPsbt));
EventManager.get().post(new ViewPSBTEvent(payjoinButton.getScene().getWindow(), headersForm.getName() + " Payjoin", proposalPsbt));
} catch(PayjoinReceiverException e) {
AppServices.showErrorDialog("Invalid Payjoin Transaction", e.getMessage());
}
@ -884,7 +884,7 @@ public class HeadersController extends TransactionFormController implements Init
@Subscribe
public void openWallets(OpenWalletsEvent event) {
if(headersForm.getPsbt() != null && headersForm.getBlockTransaction() == null) {
if((id.getScene() == null || id.getScene().getWindow().equals(event.getWindow())) && headersForm.getPsbt() != null && headersForm.getBlockTransaction() == null) {
List<Wallet> availableWallets = event.getWallets().stream().filter(wallet -> wallet.canSign(headersForm.getPsbt())).sorted(new WalletSignComparator()).collect(Collectors.toList());
Map<Wallet, Storage> availableWalletsMap = new LinkedHashMap<>(event.getWalletsMap());
availableWalletsMap.keySet().retainAll(availableWallets);

View file

@ -194,7 +194,7 @@ public class InputController extends TransactionFormController implements Initia
linkedOutpoint.setText(txInput.getOutpoint().getHash().toString() + ":" + txInput.getOutpoint().getIndex());
linkedOutpoint.setOnAction(event -> {
BlockTransaction linkedTransaction = inputTransactions.get(txInput.getOutpoint().getHash());
EventManager.get().post(new ViewTransactionEvent(linkedTransaction, TransactionView.OUTPUT, (int)txInput.getOutpoint().getIndex()));
EventManager.get().post(new ViewTransactionEvent(linkedOutpoint.getScene().getWindow(), linkedTransaction, TransactionView.OUTPUT, (int)txInput.getOutpoint().getIndex()));
});
linkedOutpoint.setContextMenu(new TransactionReferenceContextMenu(linkedOutpoint.getText()));
}

View file

@ -148,7 +148,7 @@ public class OutputController extends TransactionFormController implements Initi
final Integer inputIndex = i;
spentBy.setText(outputBlockTransaction.getHash().toString() + ":" + inputIndex);
spentBy.setOnAction(event -> {
EventManager.get().post(new ViewTransactionEvent(outputBlockTransaction, TransactionView.INPUT, inputIndex));
EventManager.get().post(new ViewTransactionEvent(spentBy.getScene().getWindow(), outputBlockTransaction, TransactionView.INPUT, inputIndex));
});
spentBy.setContextMenu(new TransactionReferenceContextMenu(spentBy.getText()));
}

View file

@ -728,7 +728,7 @@ public class SendController extends WalletFormController implements Initializabl
addWalletTransactionNodes();
createdWalletTransactionProperty.set(walletTransactionProperty.get());
PSBT psbt = walletTransactionProperty.get().createPSBT();
EventManager.get().post(new ViewPSBTEvent(walletTransactionProperty.get().getPayments().get(0).getLabel(), psbt));
EventManager.get().post(new ViewPSBTEvent(createButton.getScene().getWindow(), walletTransactionProperty.get().getPayments().get(0).getLabel(), psbt));
}
private void addWalletTransactionNodes() {

View file

@ -102,6 +102,10 @@
-fx-text-fill: #e06c75;
}
.root #noWalletsWarning .glyph-font {
-fx-text-fill: #e06c75;
}
.root .etched-raised-border {
-fx-border-color: #ffffff, #000000;
-fx-border-style: solid, solid;