improve new tx notifications

This commit is contained in:
Craig Raw 2020-08-09 14:13:10 +02:00
parent f0b7409c4a
commit b2f48a1b05
8 changed files with 91 additions and 46 deletions

2
drongo

@ -1 +1 @@
Subproject commit d2582c041479704d609c20ed13195c3f92ced999
Subproject commit fff658a3ab33a3f63f5a1cd03c2b7cc1f20bec4a

View file

@ -44,6 +44,7 @@ import javafx.scene.input.TransferMode;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.util.Duration;
import org.controlsfx.control.Notifications;
import org.controlsfx.control.StatusBar;
@ -161,9 +162,6 @@ 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();
@ -241,15 +239,6 @@ public class AppController implements Initializable {
}
openTransactionIdItem.disableProperty().bind(onlineProperty.not());
List<File> recentWalletFiles = Config.get().getRecentWalletFiles();
if(recentWalletFiles != null) {
for(File walletFile : recentWalletFiles) {
if(walletFile.exists()) {
openWalletFile(walletFile);
}
}
}
}
private ElectrumServer.ConnectionService createConnectionService() {
@ -618,7 +607,7 @@ public class AppController implements Initializable {
}
}
private void openWalletFile(File file) {
public void openWalletFile(File file) {
try {
Storage storage = new Storage(file);
FileType fileType = IOUtils.getFileType(file);
@ -959,25 +948,46 @@ public class AppController implements Initializable {
@Subscribe
public void newWalletTransactions(NewWalletTransactionsEvent event) {
if(Config.get().isNotifyNewTransactions()) {
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";
String text;
if(event.getBlockTransactions().size() == 1) {
BlockTransaction blockTransaction = event.getBlockTransactions().get(0);
if(blockTransaction.getHeight() == 0) {
text = "New mempool transaction: ";
} else {
text += String.format(Locale.ENGLISH, "%,d", event.getTotalValue()) + " sats";
int confirmations = blockTransaction.getConfirmations(getCurrentBlockHeight());
if(confirmations == 1) {
text = "First transaction confirmation: ";
} else if(confirmations <= BlockTransactionHash.BLOCKS_TO_CONFIRM) {
text = "Confirming transaction: ";
} else {
text = "Confirmed transaction: ";
}
}
text += event.getValueAsText(event.getTotalValue());
} else {
if(event.getTotalBlockchainValue() > 0 && event.getTotalMempoolValue() > 0) {
text = "New transactions: " + event.getValueAsText(event.getTotalValue()) + " (" + event.getValueAsText(event.getTotalMempoolValue()) + " in mempool)";
} else if(event.getTotalMempoolValue() > 0) {
text = "New mempool transactions: " + event.getValueAsText(event.getTotalMempoolValue());
} else {
text = "New transactions: " + event.getValueAsText(event.getTotalValue());
}
}
Window.getWindows().forEach(window -> {
String notificationStyles = AppController.class.getResource("notificationpopup.css").toExternalForm();
if(!window.getScene().getStylesheets().contains(notificationStyles)) {
window.getScene().getStylesheets().add(notificationStyles);
}
});
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))
.hideAfter(Duration.seconds(5))
.position(Pos.TOP_RIGHT)
.threshold(5, Notifications.create().title("Sparrow").text("Multiple new wallet transactions").graphic(new ImageView(image)))
.onAction(e -> selectTab(event.getWallet()));

View file

@ -7,6 +7,7 @@ import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.preferences.PreferenceGroup;
import com.sparrowwallet.sparrow.preferences.PreferencesDialog;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
@ -14,6 +15,8 @@ import javafx.scene.image.Image;
import javafx.stage.Stage;
import org.controlsfx.glyphfont.GlyphFontRegistry;
import java.io.File;
import java.util.List;
import java.util.Optional;
public class MainApp extends Application {
@ -60,6 +63,15 @@ public class MainApp extends Application {
appController.initializeView();
stage.show();
List<File> recentWalletFiles = Config.get().getRecentWalletFiles();
if(recentWalletFiles != null) {
for(File walletFile : recentWalletFiles) {
if(walletFile.exists()) {
Platform.runLater(() -> appController.openWalletFile(walletFile));
}
}
}
}
@Override

View file

@ -1,6 +1,6 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
import javafx.animation.*;
import javafx.beans.property.*;
import javafx.geometry.Pos;
@ -54,7 +54,7 @@ public class ConfirmationProgressIndicator extends StackPane {
arcLengthTimeline.getKeyFrames().add(arcLengthFrame);
sequence.getChildren().add(arcLengthTimeline);
if(newValue.intValue() == TransactionEntry.BLOCKS_TO_CONFIRM) {
if(newValue.intValue() == BlockTransactionHash.BLOCKS_TO_CONFIRM) {
Timeline arcRadiusTimeline = new Timeline();
KeyValue arcRadiusXValue = new KeyValue(arc.radiusXProperty(), 0.0);
KeyValue arcRadiusYValue = new KeyValue(arc.radiusYProperty(), 0.0);
@ -98,7 +98,7 @@ public class ConfirmationProgressIndicator extends StackPane {
}
private static double getDegrees(int confirmations) {
int requiredConfirmations = TransactionEntry.BLOCKS_TO_CONFIRM;
int requiredConfirmations = BlockTransactionHash.BLOCKS_TO_CONFIRM;
return ((double)Math.min(confirmations, requiredConfirmations)/ requiredConfirmations) * -360d;
}

View file

@ -1,19 +1,26 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.control.CoinLabel;
import com.sparrowwallet.sparrow.io.Config;
import java.util.List;
import java.util.Locale;
public class NewWalletTransactionsEvent {
private final Wallet wallet;
private final List<BlockTransaction> blockTransactions;
private final long totalValue;
private final long totalBlockchainValue;
private final long totalMempoolValue;
public NewWalletTransactionsEvent(Wallet wallet, List<BlockTransaction> blockTransactions, long totalValue) {
public NewWalletTransactionsEvent(Wallet wallet, List<BlockTransaction> blockTransactions, long totalBlockchainValue, long totalMempoolValue) {
this.wallet = wallet;
this.blockTransactions = blockTransactions;
this.totalValue = totalValue;
this.totalBlockchainValue = totalBlockchainValue;
this.totalMempoolValue = totalMempoolValue;
}
public Wallet getWallet() {
@ -25,6 +32,27 @@ public class NewWalletTransactionsEvent {
}
public long getTotalValue() {
return totalValue;
return totalBlockchainValue + totalMempoolValue;
}
public long getTotalBlockchainValue() {
return totalBlockchainValue;
}
public long getTotalMempoolValue() {
return totalMempoolValue;
}
public String getValueAsText(long value) {
BitcoinUnit unit = Config.get().getBitcoinUnit();
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
unit = (value >= BitcoinUnit.getAutoThreshold() ? BitcoinUnit.BTC : BitcoinUnit.SATOSHIS);
}
if(unit == BitcoinUnit.BTC) {
return CoinLabel.getBTCFormat().format((double) value / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
}
return String.format(Locale.ENGLISH, "%,d", value) + " sats";
}
}

View file

@ -449,7 +449,7 @@ public class HeadersController extends TransactionFormController implements Init
blockStatus.setText(confirmations + " Confirmations");
}
if(confirmations <= TransactionEntry.BLOCKS_TO_CONFIRM) {
if(confirmations <= BlockTransactionHash.BLOCKS_TO_CONFIRM) {
ConfirmationProgressIndicator indicator;
if(blockStatus.getGraphic() == null) {
indicator = new ConfirmationProgressIndicator(confirmations);

View file

@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.wallet;
import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager;
@ -17,9 +18,6 @@ import java.util.*;
import java.util.stream.Collectors;
public class TransactionEntry extends Entry implements Comparable<TransactionEntry> {
public static final int BLOCKS_TO_CONFIRM = 6;
public static final int BLOCKS_TO_FULLY_CONFIRM = 100;
private final Wallet wallet;
private final BlockTransaction blockTransaction;
@ -63,29 +61,25 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
}
public boolean isConfirming() {
return getConfirmations() < BLOCKS_TO_CONFIRM;
return getConfirmations() < BlockTransactionHash.BLOCKS_TO_CONFIRM;
}
public boolean isFullyConfirming() {
return getConfirmations() < BLOCKS_TO_FULLY_CONFIRM;
return getConfirmations() < BlockTransactionHash.BLOCKS_TO_FULLY_CONFIRM;
}
public int calculateConfirmations() {
if(blockTransaction.getHeight() <= 0) {
return 0;
}
return wallet.getStoredBlockHeight() - blockTransaction.getHeight() + 1;
return blockTransaction.getConfirmations(wallet.getStoredBlockHeight());
}
public String getConfirmationsDescription() {
int confirmations = getConfirmations();
if(confirmations == 0) {
return "Unconfirmed in mempool";
} else if(confirmations < BLOCKS_TO_FULLY_CONFIRM) {
} else if(confirmations < BlockTransactionHash.BLOCKS_TO_FULLY_CONFIRM) {
return confirmations + " confirmation" + (confirmations == 1 ? "" : "s");
} else {
return BLOCKS_TO_FULLY_CONFIRM + "+ confirmations";
return BlockTransactionHash.BLOCKS_TO_FULLY_CONFIRM + "+ confirmations";
}
}

View file

@ -69,8 +69,9 @@ public class WalletTransactionsEntry extends Entry {
if(!entriesAdded.isEmpty()) {
List<BlockTransaction> 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));
long totalBlockchainValue = entriesAdded.stream().filter(txEntry -> ((TransactionEntry)txEntry).getConfirmations() > 0).mapToLong(Entry::getValue).sum();
long totalMempoolValue = entriesAdded.stream().filter(txEntry -> ((TransactionEntry)txEntry).getConfirmations() == 0).mapToLong(Entry::getValue).sum();
EventManager.get().post(new NewWalletTransactionsEvent(wallet, blockTransactions, totalBlockchainValue, totalMempoolValue));
}
}