diff --git a/drongo b/drongo index eb07a7ff..1f7be6c7 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit eb07a7ffa3c46cda83ac951d3b1ebd529460554c +Subproject commit 1f7be6c7d5f1cdfda58617b212630575066e5496 diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 58002175..4dabbf41 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -34,14 +34,18 @@ import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.Dragboard; import javafx.scene.input.TransferMode; import javafx.scene.layout.StackPane; import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.util.Duration; +import org.controlsfx.control.Notifications; import org.controlsfx.control.StatusBar; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,6 +55,7 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.util.*; +import java.util.List; import java.util.stream.Collectors; public class AppController implements Initializable { @@ -154,6 +159,9 @@ public class AppController implements Initializable { rootStack.getStyleClass().removeAll(DRAG_OVER_CLASS); }); + Stage tabStage = (Stage)tabs.getScene().getWindow(); + tabStage.getScene().getStylesheets().add(AppController.class.getResource("notificationpopup.css").toExternalForm()); + tabs.getSelectionModel().selectedItemProperty().addListener((observable, old_val, selectedTab) -> { if(selectedTab != null) { TabData tabData = (TabData)selectedTab.getUserData(); @@ -526,6 +534,18 @@ public class AppController implements Initializable { alert.showAndWait(); } + public void selectTab(Wallet wallet) { + for(Tab tab : tabs.getTabs()) { + TabData tabData = (TabData) tab.getUserData(); + if(tabData.getType() == TabData.TabType.WALLET) { + WalletTabData walletTabData = (WalletTabData) tabData; + if(walletTabData.getWallet() == wallet) { + tabs.getSelectionModel().select(tab); + } + } + } + } + public void closeTab(ActionEvent event) { tabs.getTabs().remove(tabs.getSelectionModel().getSelectedItem()); } @@ -904,6 +924,35 @@ public class AppController implements Initializable { exportWallet.setDisable(!event.getWallet().isValid()); } + @Subscribe + public void newWalletTransactions(NewWalletTransactionsEvent event) { + String text = "New " + (event.getBlockTransactions().size() > 1 ? "transactions: " : "transaction: "); + + BitcoinUnit unit = Config.get().getBitcoinUnit(); + if(unit == null || unit.equals(BitcoinUnit.AUTO)) { + unit = (event.getTotalValue() >= BitcoinUnit.getAutoThreshold() ? BitcoinUnit.BTC : BitcoinUnit.SATOSHIS); + } + + if(unit == BitcoinUnit.BTC) { + text += CoinLabel.getBTCFormat().format((double)event.getTotalValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC"; + } else { + text += String.format(Locale.ENGLISH, "%,d", event.getTotalValue()) + " sats"; + } + + Image image = new Image("image/sparrow-small.png", 50, 50, false, false); + Notifications notificationBuilder = Notifications.create() + .title("Sparrow - " + event.getWallet().getName()) + .text(text) + .graphic(new ImageView(image)) + .hideAfter(Duration.seconds(180)) + .position(Pos.TOP_RIGHT) + .hideCloseButton() + .threshold(5, Notifications.create().title("Sparrow").text("Multiple new transactions").graphic(new ImageView(image))) + .onAction(e -> selectTab(event.getWallet())); + + notificationBuilder.show(); + } + @Subscribe public void statusUpdated(StatusEvent event) { statusBar.setText(event.getStatus()); diff --git a/src/main/java/com/sparrowwallet/sparrow/event/NewWalletTransactionsEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/NewWalletTransactionsEvent.java new file mode 100644 index 00000000..61b581d4 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/event/NewWalletTransactionsEvent.java @@ -0,0 +1,30 @@ +package com.sparrowwallet.sparrow.event; + +import com.sparrowwallet.drongo.wallet.BlockTransaction; +import com.sparrowwallet.drongo.wallet.Wallet; + +import java.util.List; + +public class NewWalletTransactionsEvent { + private final Wallet wallet; + private final List blockTransactions; + private final long totalValue; + + public NewWalletTransactionsEvent(Wallet wallet, List blockTransactions, long totalValue) { + this.wallet = wallet; + this.blockTransactions = blockTransactions; + this.totalValue = totalValue; + } + + public Wallet getWallet() { + return wallet; + } + + public List getBlockTransactions() { + return blockTransactions; + } + + public long getTotalValue() { + return totalValue; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java index 0bbe61fe..934917cb 100644 --- a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java +++ b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java @@ -17,6 +17,7 @@ public class FontAwesome5 extends GlyphFont { public static enum Glyph implements INamedCharacter { ARROW_DOWN('\uf063'), ARROW_UP('\uf062'), + BTC('\uf15a'), CAMERA('\uf030'), CHECK_CIRCLE('\uf058'), CIRCLE('\uf111'), diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java index e5cb1f42..8a7e713f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletTransactionsEntry.java @@ -5,6 +5,8 @@ import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.WalletNode; +import com.sparrowwallet.sparrow.EventManager; +import com.sparrowwallet.sparrow.event.NewWalletTransactionsEvent; import javafx.beans.property.LongProperty; import javafx.beans.property.LongPropertyBase; @@ -64,6 +66,12 @@ public class WalletTransactionsEntry extends Entry { getChildren().removeAll(entriesRemoved); calculateBalances(); + + if(!entriesAdded.isEmpty()) { + List blockTransactions = entriesAdded.stream().map(txEntry -> ((TransactionEntry)txEntry).getBlockTransaction()).collect(Collectors.toList()); + long totalValue = entriesAdded.stream().mapToLong(Entry::getValue).sum(); + EventManager.get().post(new NewWalletTransactionsEvent(wallet, blockTransactions, totalValue)); + } } private static Collection getWalletTransactions(Wallet wallet) { diff --git a/src/main/resources/com/sparrowwallet/sparrow/notificationpopup.css b/src/main/resources/com/sparrowwallet/sparrow/notificationpopup.css new file mode 100644 index 00000000..d6ac3de8 --- /dev/null +++ b/src/main/resources/com/sparrowwallet/sparrow/notificationpopup.css @@ -0,0 +1,29 @@ + +.notification-bar > .pane { + -fx-padding: 0 7 7 7; +} + +.notification-bar > .close-button { + -fx-min-height: 27; + -fx-padding: 10 0 0 0; + -fx-translate-y: 10; +} + +.notification-bar > .pane .label.title { + -fx-font-size: 15px; + -fx-text-fill: #292929; + -fx-padding: 5 0 0 68; + -fx-translate-y: 10; +} + +.notification-bar > .pane .label { + -fx-font-size: 1em; + -fx-text-fill: #292929; + -fx-alignment: top-left; + -fx-padding: 0 0 0 10; + -fx-graphic-text-gap: 8; +} + +.notification-bar > .pane .label .text { + -fx-translate-y: 12; +} \ No newline at end of file diff --git a/src/main/resources/image/sparrow-small@2x.png b/src/main/resources/image/sparrow-small@2x.png new file mode 100644 index 00000000..45a7efa7 Binary files /dev/null and b/src/main/resources/image/sparrow-small@2x.png differ diff --git a/src/main/resources/image/sparrow-small@3x.png b/src/main/resources/image/sparrow-small@3x.png new file mode 100644 index 00000000..be9a6a1a Binary files /dev/null and b/src/main/resources/image/sparrow-small@3x.png differ