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 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); boolean walletRemoved = c.getRemoved().stream().anyMatch(tab -> ((TabData)tab.getUserData()).getType() == TabData.TabType.WALLET);
if(walletAdded || walletRemoved) { 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()) 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."); showErrorDialog("Invalid transaction ID", "A transaction with that ID could not be found.");
} else { } else {
Platform.runLater(() -> { Platform.runLater(() -> {
EventManager.get().post(new ViewTransactionEvent(blockTransaction)); EventManager.get().post(new ViewTransactionEvent(tabs.getScene().getWindow(), blockTransaction));
}); });
} }
}); });
@ -1000,7 +1000,7 @@ public class AppController implements Initializable {
@Subscribe @Subscribe
public void tabSelected(TabSelectedEvent event) { public void tabSelected(TabSelectedEvent event) {
//TODO: Handle multiple windows if(tabs.getTabs().contains(event.getTab())) {
String tabName = event.getTabName(); String tabName = event.getTabName();
if(tabs.getScene() != null) { if(tabs.getScene() != null) {
Stage tabStage = (Stage)tabs.getScene().getWindow(); Stage tabStage = (Stage)tabs.getScene().getWindow();
@ -1023,6 +1023,7 @@ public class AppController implements Initializable {
showTxHex.setDisable(true); showTxHex.setDisable(true);
} }
} }
}
@Subscribe @Subscribe
public void transactionExtractedEvent(TransactionExtractedEvent event) { public void transactionExtractedEvent(TransactionExtractedEvent event) {
@ -1039,14 +1040,19 @@ public class AppController implements Initializable {
@Subscribe @Subscribe
public void walletSettingsChanged(WalletSettingsChangedEvent event) { public void walletSettingsChanged(WalletSettingsChangedEvent event) {
//TODO: Handle multiple windows 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()); exportWallet.setDisable(!event.getWallet().isValid());
} }
}
}
@Subscribe @Subscribe
public void newWalletTransactions(NewWalletTransactionsEvent event) { public void newWalletTransactions(NewWalletTransactionsEvent event) {
//TODO: Handle multiple windows if(Config.get().isNotifyNewTransactions() && getOpenWallets().containsKey(event.getWallet())) {
if(Config.get().isNotifyNewTransactions()) {
String text; String text;
if(event.getBlockTransactions().size() == 1) { if(event.getBlockTransactions().size() == 1) {
BlockTransaction blockTransaction = event.getBlockTransactions().get(0); BlockTransaction blockTransaction = event.getBlockTransactions().get(0);
@ -1184,17 +1190,19 @@ public class AppController implements Initializable {
@Subscribe @Subscribe
public void viewTransaction(ViewTransactionEvent event) { public void viewTransaction(ViewTransactionEvent event) {
//TODO: Handle multiple windows if(tabs.getScene().getWindow().equals(event.getWindow())) {
Tab tab = addTransactionTab(event.getBlockTransaction(), event.getInitialView(), event.getInitialIndex()); Tab tab = addTransactionTab(event.getBlockTransaction(), event.getInitialView(), event.getInitialIndex());
tabs.getSelectionModel().select(tab); tabs.getSelectionModel().select(tab);
} }
}
@Subscribe @Subscribe
public void viewPSBT(ViewPSBTEvent event) { public void viewPSBT(ViewPSBTEvent event) {
//TODO: Handle multiple windows if(tabs.getScene().getWindow().equals(event.getWindow())) {
Tab tab = addTransactionTab(event.getLabel(), event.getPsbt()); Tab tab = addTransactionTab(event.getLabel(), event.getPsbt());
tabs.getSelectionModel().select(tab); tabs.getSelectionModel().select(tab);
} }
}
@Subscribe @Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) { public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
@ -1204,25 +1212,27 @@ public class AppController implements Initializable {
@Subscribe @Subscribe
public void requestOpenWallets(RequestOpenWalletsEvent event) { public void requestOpenWallets(RequestOpenWalletsEvent event) {
//TODO: Handle multiple windows EventManager.get().post(new OpenWalletsEvent(tabs.getScene().getWindow(), getOpenWallets()));
EventManager.get().post(new OpenWalletsEvent(getOpenWallets()));
} }
@Subscribe @Subscribe
public void requestWalletOpen(RequestWalletOpenEvent event) { public void requestWalletOpen(RequestWalletOpenEvent event) {
//TODO: Handle multiple windows if(tabs.getScene().getWindow().equals(event.getWindow())) {
openWallet(null); openWallet(null);
} }
}
@Subscribe @Subscribe
public void requestTransactionOpen(RequestTransactionOpenEvent event) { public void requestTransactionOpen(RequestTransactionOpenEvent event) {
//TODO: Handle multiple windows if(tabs.getScene().getWindow().equals(event.getWindow())) {
openTransactionFromFile(null); openTransactionFromFile(null);
} }
}
@Subscribe @Subscribe
public void requestQRScan(RequestQRScanEvent event) { public void requestQRScan(RequestQRScanEvent event) {
//TODO: Handle multiple windows if(tabs.getScene().getWindow().equals(event.getWindow())) {
openTransactionFromQR(null); openTransactionFromQR(null);
} }
}
} }

View file

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

View file

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

View file

@ -304,7 +304,8 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
public void openWallets(OpenWalletsEvent event) { public void openWallets(OpenWalletsEvent event) {
Storage storage = event.getStorage(wallet); Storage storage = event.getStorage(wallet);
if(storage == null) { if(storage == null) {
throw new IllegalStateException("Wallet " + wallet + " without Storage"); //Another window, ignore
return;
} }
WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD); 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.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
import javafx.stage.Window;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class OpenWalletsEvent { public class OpenWalletsEvent {
private final Window window;
private final Map<Wallet, Storage> walletsMap; 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; this.walletsMap = walletsMap;
} }
public Window getWindow() {
return window;
}
public List<Wallet> getWallets() { public List<Wallet> getWallets() {
return new ArrayList<>(walletsMap.keySet()); return new ArrayList<>(walletsMap.keySet());
} }

View file

@ -1,5 +1,18 @@
package com.sparrowwallet.sparrow.event; package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
/**
* Event class used to request the QRScanDialog is opened
*/
public class RequestQRScanEvent { 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; package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
/**
* Event class used to request the transaction open file dialog
*/
public class RequestTransactionOpenEvent { 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; package com.sparrowwallet.sparrow.event;
import javafx.stage.Window;
/**
* Event class used to request the wallet open dialog
*/
public class RequestWalletOpenEvent { 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.drongo.psbt.PSBT;
import com.sparrowwallet.sparrow.transaction.TransactionView; import com.sparrowwallet.sparrow.transaction.TransactionView;
import javafx.stage.Window;
public class ViewPSBTEvent { public class ViewPSBTEvent {
private final Window window;
private final String label; private final String label;
private final PSBT psbt; private final PSBT psbt;
private final TransactionView initialView; private final TransactionView initialView;
private final Integer initialIndex; private final Integer initialIndex;
public ViewPSBTEvent(String label, PSBT psbt) { public ViewPSBTEvent(Window window, String label, PSBT psbt) {
this(label, psbt, TransactionView.HEADERS, null); 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.label = label;
this.psbt = psbt; this.psbt = psbt;
this.initialView = initialView; this.initialView = initialView;
this.initialIndex = initialIndex; this.initialIndex = initialIndex;
} }
public Window getWindow() {
return window;
}
public String getLabel() { public String getLabel() {
return label; return label;
} }

View file

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

View file

@ -609,7 +609,7 @@ public class HeadersController extends TransactionFormController implements Init
} }
public void openWallet(ActionEvent event) { public void openWallet(ActionEvent event) {
EventManager.get().post(new RequestWalletOpenEvent()); EventManager.get().post(new RequestWalletOpenEvent(noWalletsWarningLink.getScene().getWindow()));
} }
public void finalizeTransaction(ActionEvent event) { public void finalizeTransaction(ActionEvent event) {
@ -635,7 +635,7 @@ public class HeadersController extends TransactionFormController implements Init
ToggleButton toggleButton = (ToggleButton)event.getSource(); ToggleButton toggleButton = (ToggleButton)event.getSource();
toggleButton.setSelected(false); toggleButton.setSelected(false);
EventManager.get().post(new RequestQRScanEvent()); EventManager.get().post(new RequestQRScanEvent(toggleButton.getScene().getWindow()));
} }
public void savePSBT(ActionEvent event) { public void savePSBT(ActionEvent event) {
@ -668,7 +668,7 @@ public class HeadersController extends TransactionFormController implements Init
ToggleButton toggleButton = (ToggleButton)event.getSource(); ToggleButton toggleButton = (ToggleButton)event.getSource();
toggleButton.setSelected(false); toggleButton.setSelected(false);
EventManager.get().post(new RequestTransactionOpenEvent()); EventManager.get().post(new RequestTransactionOpenEvent(toggleButton.getScene().getWindow()));
} }
public void signPSBT(ActionEvent event) { public void signPSBT(ActionEvent event) {
@ -830,7 +830,7 @@ public class HeadersController extends TransactionFormController implements Init
try { try {
Payjoin payjoin = new Payjoin(payjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt()); Payjoin payjoin = new Payjoin(payjoinURI, headersForm.getSigningWallet(), headersForm.getPsbt());
PSBT proposalPsbt = payjoin.requestPayjoinPSBT(true); 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) { } catch(PayjoinReceiverException e) {
AppServices.showErrorDialog("Invalid Payjoin Transaction", e.getMessage()); AppServices.showErrorDialog("Invalid Payjoin Transaction", e.getMessage());
} }
@ -884,7 +884,7 @@ public class HeadersController extends TransactionFormController implements Init
@Subscribe @Subscribe
public void openWallets(OpenWalletsEvent event) { 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()); 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()); Map<Wallet, Storage> availableWalletsMap = new LinkedHashMap<>(event.getWalletsMap());
availableWalletsMap.keySet().retainAll(availableWallets); 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.setText(txInput.getOutpoint().getHash().toString() + ":" + txInput.getOutpoint().getIndex());
linkedOutpoint.setOnAction(event -> { linkedOutpoint.setOnAction(event -> {
BlockTransaction linkedTransaction = inputTransactions.get(txInput.getOutpoint().getHash()); 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())); linkedOutpoint.setContextMenu(new TransactionReferenceContextMenu(linkedOutpoint.getText()));
} }

View file

@ -148,7 +148,7 @@ public class OutputController extends TransactionFormController implements Initi
final Integer inputIndex = i; final Integer inputIndex = i;
spentBy.setText(outputBlockTransaction.getHash().toString() + ":" + inputIndex); spentBy.setText(outputBlockTransaction.getHash().toString() + ":" + inputIndex);
spentBy.setOnAction(event -> { 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())); spentBy.setContextMenu(new TransactionReferenceContextMenu(spentBy.getText()));
} }

View file

@ -728,7 +728,7 @@ public class SendController extends WalletFormController implements Initializabl
addWalletTransactionNodes(); addWalletTransactionNodes();
createdWalletTransactionProperty.set(walletTransactionProperty.get()); createdWalletTransactionProperty.set(walletTransactionProperty.get());
PSBT psbt = walletTransactionProperty.get().createPSBT(); 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() { private void addWalletTransactionNodes() {

View file

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