mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
wallet tx confirmation indicator and event refactor
This commit is contained in:
parent
21f642bb5c
commit
a8f16c15e0
14 changed files with 340 additions and 50 deletions
|
@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.control;
|
||||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
import com.sparrowwallet.sparrow.wallet.Entry;
|
import com.sparrowwallet.sparrow.wallet.Entry;
|
||||||
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
|
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
|
||||||
|
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.scene.control.Tooltip;
|
import javafx.scene.control.Tooltip;
|
||||||
import javafx.scene.control.TreeTableCell;
|
import javafx.scene.control.TreeTableCell;
|
||||||
|
@ -14,7 +15,6 @@ class AmountCell extends TreeTableCell<Entry, Long> {
|
||||||
public AmountCell() {
|
public AmountCell() {
|
||||||
super();
|
super();
|
||||||
getStyleClass().add("amount-cell");
|
getStyleClass().add("amount-cell");
|
||||||
setContentDisplay(ContentDisplay.RIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,16 +25,37 @@ class AmountCell extends TreeTableCell<Entry, Long> {
|
||||||
setText(null);
|
setText(null);
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
} else {
|
} else {
|
||||||
EntryCell.applyRowStyles(this, getTreeTableView().getTreeItem(getIndex()).getValue());
|
Entry entry = getTreeTableView().getTreeItem(getIndex()).getValue();
|
||||||
|
EntryCell.applyRowStyles(this, entry);
|
||||||
|
|
||||||
String satsValue = String.format(Locale.ENGLISH, "%,d", amount);
|
String satsValue = String.format(Locale.ENGLISH, "%,d", amount);
|
||||||
String btcValue = CoinLabel.getBTCFormat().format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
|
final String btcValue = CoinLabel.getBTCFormat().format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
|
||||||
|
|
||||||
Entry entry = getTreeTableView().getTreeItem(getIndex()).getValue();
|
if(entry instanceof TransactionEntry) {
|
||||||
if(entry instanceof HashIndexEntry) {
|
TransactionEntry transactionEntry = (TransactionEntry)entry;
|
||||||
|
Tooltip tooltip = new Tooltip();
|
||||||
|
tooltip.setText(btcValue + " (" + transactionEntry.getConfirmationsDescription() + ")");
|
||||||
|
setTooltip(tooltip);
|
||||||
|
|
||||||
|
transactionEntry.confirmationsProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
Tooltip newTooltip = new Tooltip();
|
||||||
|
newTooltip.setText(btcValue + " (" + transactionEntry.getConfirmationsDescription() + ")");
|
||||||
|
setTooltip(newTooltip);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(transactionEntry.isConfirming()) {
|
||||||
|
ConfirmationProgressIndicator arc = new ConfirmationProgressIndicator(transactionEntry.getConfirmations());
|
||||||
|
arc.confirmationsProperty().bind(transactionEntry.confirmationsProperty());
|
||||||
|
setGraphic(arc);
|
||||||
|
setContentDisplay(ContentDisplay.LEFT);
|
||||||
|
} else {
|
||||||
|
setGraphic(null);
|
||||||
|
}
|
||||||
|
} else if(entry instanceof HashIndexEntry) {
|
||||||
Region node = new Region();
|
Region node = new Region();
|
||||||
node.setPrefWidth(10);
|
node.setPrefWidth(10);
|
||||||
setGraphic(node);
|
setGraphic(node);
|
||||||
|
setContentDisplay(ContentDisplay.RIGHT);
|
||||||
|
|
||||||
if(((HashIndexEntry) entry).getType() == HashIndexEntry.Type.INPUT) {
|
if(((HashIndexEntry) entry).getType() == HashIndexEntry.Type.INPUT) {
|
||||||
satsValue = "-" + satsValue;
|
satsValue = "-" + satsValue;
|
||||||
|
@ -43,11 +64,14 @@ class AmountCell extends TreeTableCell<Entry, Long> {
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tooltip tooltip = new Tooltip();
|
if(getTooltip() == null) {
|
||||||
tooltip.setText(btcValue);
|
Tooltip tooltip = new Tooltip();
|
||||||
|
tooltip.setText(btcValue);
|
||||||
|
setTooltip(tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
setText(satsValue);
|
setText(satsValue);
|
||||||
setTooltip(tooltip);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
|
||||||
|
import javafx.animation.*;
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Group;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Arc;
|
||||||
|
import javafx.scene.shape.ArcType;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
import javafx.scene.shape.Line;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
public class ConfirmationProgressIndicator extends StackPane {
|
||||||
|
private final Group confirmationGroup;
|
||||||
|
private final Arc arc;
|
||||||
|
private final Line downTickLine;
|
||||||
|
private final Line upTickLine;
|
||||||
|
|
||||||
|
public ConfirmationProgressIndicator(int confirmations) {
|
||||||
|
Circle circle = new Circle(7, 7, 7);
|
||||||
|
circle.setFill(Color.TRANSPARENT);
|
||||||
|
circle.getStyleClass().add("confirmation-progress-circle");
|
||||||
|
|
||||||
|
arc = new Arc(7, 7, 7, 7, 90, getDegrees(confirmations));
|
||||||
|
arc.setType(ArcType.ROUND);
|
||||||
|
arc.getStyleClass().add("confirmation-progress-arc");
|
||||||
|
|
||||||
|
downTickLine = new Line(4, 8, 4, 8);
|
||||||
|
downTickLine.setStrokeWidth(1.0);
|
||||||
|
downTickLine.setOpacity(0);
|
||||||
|
downTickLine.getStyleClass().add("confirmation-progress-tick");
|
||||||
|
upTickLine = new Line(6, 10, 6, 10);
|
||||||
|
upTickLine.setStrokeWidth(1.0);
|
||||||
|
upTickLine.setOpacity(0);
|
||||||
|
upTickLine.getStyleClass().add("confirmation-progress-tick");
|
||||||
|
|
||||||
|
confirmationGroup = new Group(circle, arc, downTickLine, upTickLine);
|
||||||
|
getStyleClass().add("confirmation-progress");
|
||||||
|
|
||||||
|
setAlignment(Pos.CENTER);
|
||||||
|
getChildren().addAll(confirmationGroup);
|
||||||
|
|
||||||
|
confirmationsProperty().set(confirmations);
|
||||||
|
confirmationsProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if(!oldValue.equals(newValue)) {
|
||||||
|
SequentialTransition sequence = new SequentialTransition();
|
||||||
|
|
||||||
|
Timeline arcLengthTimeline = new Timeline();
|
||||||
|
KeyValue arcLengthValue = new KeyValue(arc.lengthProperty(), getDegrees(newValue.intValue()));
|
||||||
|
KeyFrame arcLengthFrame = new KeyFrame(Duration.millis(1000), arcLengthValue);
|
||||||
|
arcLengthTimeline.getKeyFrames().add(arcLengthFrame);
|
||||||
|
sequence.getChildren().add(arcLengthTimeline);
|
||||||
|
|
||||||
|
if(newValue.intValue() == TransactionEntry.BLOCKS_TO_CONFIRM) {
|
||||||
|
Timeline arcRadiusTimeline = new Timeline();
|
||||||
|
KeyValue arcRadiusXValue = new KeyValue(arc.radiusXProperty(), 0.0);
|
||||||
|
KeyValue arcRadiusYValue = new KeyValue(arc.radiusYProperty(), 0.0);
|
||||||
|
KeyFrame arcRadiusFrame = new KeyFrame(Duration.millis(500), arcRadiusXValue, arcRadiusYValue);
|
||||||
|
arcRadiusTimeline.getKeyFrames().add(arcRadiusFrame);
|
||||||
|
sequence.getChildren().add(arcRadiusTimeline);
|
||||||
|
|
||||||
|
FadeTransition downTickFadeIn = new FadeTransition(Duration.millis(50), downTickLine);
|
||||||
|
downTickFadeIn.setFromValue(0);
|
||||||
|
downTickFadeIn.setToValue(1);
|
||||||
|
sequence.getChildren().add(downTickFadeIn);
|
||||||
|
|
||||||
|
Timeline downTickLineTimeline = new Timeline();
|
||||||
|
KeyValue downTickLineX = new KeyValue(downTickLine.endXProperty(), 6);
|
||||||
|
KeyValue downTickLineY = new KeyValue(downTickLine.endYProperty(), 10);
|
||||||
|
KeyFrame downTickLineFrame = new KeyFrame(Duration.millis(125), downTickLineX, downTickLineY);
|
||||||
|
downTickLineTimeline.getKeyFrames().add(downTickLineFrame);
|
||||||
|
sequence.getChildren().add(downTickLineTimeline);
|
||||||
|
|
||||||
|
FadeTransition upTickFadeIn = new FadeTransition(Duration.millis(50), upTickLine);
|
||||||
|
upTickFadeIn.setFromValue(0);
|
||||||
|
upTickFadeIn.setToValue(1);
|
||||||
|
sequence.getChildren().add(upTickFadeIn);
|
||||||
|
|
||||||
|
Timeline upTickLineTimeline = new Timeline();
|
||||||
|
KeyValue upTickLineX = new KeyValue(upTickLine.endXProperty(), 10);
|
||||||
|
KeyValue upTickLineY = new KeyValue(upTickLine.endYProperty(), 4);
|
||||||
|
KeyFrame upTickLineFrame = new KeyFrame(Duration.millis(250), upTickLineX, upTickLineY);
|
||||||
|
upTickLineTimeline.getKeyFrames().add(upTickLineFrame);
|
||||||
|
sequence.getChildren().add(upTickLineTimeline);
|
||||||
|
|
||||||
|
FadeTransition groupFadeOut = new FadeTransition(Duration.minutes(1), confirmationGroup);
|
||||||
|
groupFadeOut.setFromValue(1);
|
||||||
|
groupFadeOut.setToValue(0);
|
||||||
|
sequence.getChildren().add(groupFadeOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence.play();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getDegrees(int confirmations) {
|
||||||
|
int requiredConfirmations = TransactionEntry.BLOCKS_TO_CONFIRM;
|
||||||
|
return ((double)Math.min(confirmations, requiredConfirmations)/ requiredConfirmations) * -360d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the number of confirmations
|
||||||
|
*/
|
||||||
|
private IntegerProperty confirmations;
|
||||||
|
|
||||||
|
public final void setConfirmations(int value) {
|
||||||
|
if(confirmations != null || value != 0) {
|
||||||
|
confirmationsProperty().set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getConfirmations() {
|
||||||
|
return confirmations == null ? 0 : confirmations.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final IntegerProperty confirmationsProperty() {
|
||||||
|
if(confirmations == null) {
|
||||||
|
confirmations = new IntegerPropertyBase(0) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBean() {
|
||||||
|
return ConfirmationProgressIndicator.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "confirmations";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return confirmations;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,17 +2,16 @@ package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
import com.sparrowwallet.drongo.address.Address;
|
import com.sparrowwallet.drongo.address.Address;
|
||||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
|
||||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.ReceiveActionEvent;
|
import com.sparrowwallet.sparrow.event.ReceiveActionEvent;
|
||||||
import com.sparrowwallet.sparrow.event.ReceiveToEvent;
|
import com.sparrowwallet.sparrow.event.ReceiveToEvent;
|
||||||
import com.sparrowwallet.sparrow.event.ViewTransactionEvent;
|
import com.sparrowwallet.sparrow.event.ViewTransactionEvent;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
import com.sparrowwallet.sparrow.transaction.TransactionView;
|
import com.sparrowwallet.sparrow.wallet.Entry;
|
||||||
import com.sparrowwallet.sparrow.wallet.*;
|
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
|
||||||
|
import com.sparrowwallet.sparrow.wallet.NodeEntry;
|
||||||
|
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
@ -185,6 +184,15 @@ class EntryCell extends TreeTableCell<Entry, Entry> {
|
||||||
if(entry != null) {
|
if(entry != null) {
|
||||||
if(entry instanceof TransactionEntry) {
|
if(entry instanceof TransactionEntry) {
|
||||||
cell.getStyleClass().add("transaction-row");
|
cell.getStyleClass().add("transaction-row");
|
||||||
|
TransactionEntry transactionEntry = (TransactionEntry)entry;
|
||||||
|
if(transactionEntry.isConfirming()) {
|
||||||
|
cell.getStyleClass().add("confirming");
|
||||||
|
transactionEntry.confirmationsProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if(!transactionEntry.isConfirming()) {
|
||||||
|
cell.getStyleClass().remove("confirming");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if(entry instanceof NodeEntry) {
|
} else if(entry instanceof NodeEntry) {
|
||||||
cell.getStyleClass().add("node-row");
|
cell.getStyleClass().add("node-row");
|
||||||
} else if(entry instanceof HashIndexEntry) {
|
} else if(entry instanceof HashIndexEntry) {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.sparrow.wallet.Entry;
|
||||||
|
|
||||||
|
public class WalletEntryLabelChangedEvent extends WalletChangedEvent {
|
||||||
|
private final Entry entry;
|
||||||
|
|
||||||
|
public WalletEntryLabelChangedEvent(Wallet wallet, Entry entry) {
|
||||||
|
super(wallet);
|
||||||
|
this.entry = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry getEntry() {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,11 +11,11 @@ import java.util.stream.Collectors;
|
||||||
* This is posted by WalletForm once the history of the wallet has been refreshed, and new transactions detected
|
* This is posted by WalletForm once the history of the wallet has been refreshed, and new transactions detected
|
||||||
* Extends WalletChangedEvent so also saves the wallet.
|
* Extends WalletChangedEvent so also saves the wallet.
|
||||||
*/
|
*/
|
||||||
public class WalletHistoryChangedEvent extends WalletChangedEvent {
|
public class WalletHistoryChangedEvent extends WalletBlockHeightChangedEvent {
|
||||||
private final List<WalletNode> historyChangedNodes;
|
private final List<WalletNode> historyChangedNodes;
|
||||||
|
|
||||||
public WalletHistoryChangedEvent(Wallet wallet, List<WalletNode> historyChangedNodes) {
|
public WalletHistoryChangedEvent(Wallet wallet, Integer blockHeight, List<WalletNode> historyChangedNodes) {
|
||||||
super(wallet);
|
super(wallet, blockHeight);
|
||||||
this.historyChangedNodes = historyChangedNodes;
|
this.historyChangedNodes = historyChangedNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,10 @@ import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is posted by WalletForm once it has received a WalletSettingsChangedEvent and cleared it's entry caches
|
* This event is posted by WalletForm once it has received a WalletSettingsChangedEvent and cleared it's entry caches
|
||||||
* It does not extend WalletChangedEvent for the same reason WalletSettingsChangedEvent does not - it does not want to trigger a wallet save.
|
* The controllers in the wallet package listen to this event to update their views should a wallet's settings change
|
||||||
*/
|
*/
|
||||||
public class WalletNodesChangedEvent {
|
public class WalletNodesChangedEvent extends WalletChangedEvent {
|
||||||
private final Wallet wallet;
|
|
||||||
|
|
||||||
public WalletNodesChangedEvent(Wallet wallet) {
|
public WalletNodesChangedEvent(Wallet wallet) {
|
||||||
this.wallet = wallet;
|
super(wallet);
|
||||||
}
|
|
||||||
|
|
||||||
public Wallet getWallet() {
|
|
||||||
return wallet;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,18 @@ import java.io.File;
|
||||||
/**
|
/**
|
||||||
* This event is posted when a wallet's settings are changed (keystores, policy, script type).
|
* This event is posted when a wallet's settings are changed (keystores, policy, script type).
|
||||||
* This event marks a fundamental change that is used to update application level UI, clear node entry caches and similar. It should only be subscribed to by application-level classes.
|
* This event marks a fundamental change that is used to update application level UI, clear node entry caches and similar. It should only be subscribed to by application-level classes.
|
||||||
* It does not extend WalletChangedEvent since that is used to save the wallet, and the wallet is saved directly in SettingsController before this event is posted.
|
* Note that WalletForm does not listen to this event to save the wallet, since the wallet is foreground saved directly in SettingsController before this event is posted.
|
||||||
* Note that all wallet detail controllers that share a WalletForm, that class posts WalletNodesChangedEvent once it has cleared it's entry caches.
|
* This is because any failure in saving the wallet must be immediately reported to the user.
|
||||||
|
* Note that all wallet detail controllers that share a WalletForm, and that class posts WalletNodesChangedEvent once it has cleared it's entry caches.
|
||||||
*/
|
*/
|
||||||
public class WalletSettingsChangedEvent {
|
public class WalletSettingsChangedEvent extends WalletChangedEvent {
|
||||||
private final Wallet wallet;
|
|
||||||
private final File walletFile;
|
private final File walletFile;
|
||||||
|
|
||||||
public WalletSettingsChangedEvent(Wallet wallet, File walletFile) {
|
public WalletSettingsChangedEvent(Wallet wallet, File walletFile) {
|
||||||
this.wallet = wallet;
|
super(wallet);
|
||||||
this.walletFile = walletFile;
|
this.walletFile = walletFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Wallet getWallet() {
|
|
||||||
return wallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getWalletFile() {
|
public File getWalletFile() {
|
||||||
return walletFile;
|
return walletFile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.control.DateLabel;
|
import com.sparrowwallet.sparrow.control.DateLabel;
|
||||||
import com.sparrowwallet.sparrow.event.WalletChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -27,7 +27,7 @@ public class HashIndexEntry extends Entry implements Comparable<HashIndexEntry>
|
||||||
|
|
||||||
labelProperty().addListener((observable, oldValue, newValue) -> {
|
labelProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
hashIndex.setLabel(newValue);
|
hashIndex.setLabel(newValue);
|
||||||
EventManager.get().post(new WalletChangedEvent(wallet));
|
EventManager.get().post(new WalletEntryLabelChangedEvent(wallet, this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import com.sparrowwallet.drongo.protocol.Script;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.WalletChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent;
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ public class NodeEntry extends Entry {
|
||||||
|
|
||||||
labelProperty().addListener((observable, oldValue, newValue) -> {
|
labelProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
node.setLabel(newValue);
|
node.setLabel(newValue);
|
||||||
EventManager.get().post(new WalletChangedEvent(wallet));
|
EventManager.get().post(new WalletEntryLabelChangedEvent(wallet, this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
package com.sparrowwallet.sparrow.wallet;
|
package com.sparrowwallet.sparrow.wallet;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sparrowwallet.drongo.KeyPurpose;
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
|
||||||
import com.sparrowwallet.drongo.protocol.TransactionInput;
|
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.WalletChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.IntegerPropertyBase;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class TransactionEntry extends Entry implements Comparable<TransactionEntry> {
|
public class TransactionEntry extends Entry implements Comparable<TransactionEntry> {
|
||||||
private static final int BLOCKS_TO_CONFIRM = 6;
|
public static final int BLOCKS_TO_CONFIRM = 6;
|
||||||
|
public static final int BLOCKS_TO_FULLY_CONFIRM = 100;
|
||||||
|
|
||||||
private final Wallet wallet;
|
private final Wallet wallet;
|
||||||
private final BlockTransaction blockTransaction;
|
private final BlockTransaction blockTransaction;
|
||||||
|
@ -27,8 +29,13 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
|
||||||
|
|
||||||
labelProperty().addListener((observable, oldValue, newValue) -> {
|
labelProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
blockTransaction.setLabel(newValue);
|
blockTransaction.setLabel(newValue);
|
||||||
EventManager.get().post(new WalletChangedEvent(wallet));
|
EventManager.get().post(new WalletEntryLabelChangedEvent(wallet, this));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setConfirmations(calculateConfirmations());
|
||||||
|
if(isFullyConfirming()) {
|
||||||
|
EventManager.get().register(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Wallet getWallet() {
|
public Wallet getWallet() {
|
||||||
|
@ -66,7 +73,11 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
|
||||||
return getConfirmations() < BLOCKS_TO_CONFIRM;
|
return getConfirmations() < BLOCKS_TO_CONFIRM;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getConfirmations() {
|
public boolean isFullyConfirming() {
|
||||||
|
return getConfirmations() < BLOCKS_TO_FULLY_CONFIRM;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int calculateConfirmations() {
|
||||||
if(blockTransaction.getHeight() == 0) {
|
if(blockTransaction.getHeight() == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -74,6 +85,17 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
|
||||||
return wallet.getStoredBlockHeight() - blockTransaction.getHeight() + 1;
|
return wallet.getStoredBlockHeight() - blockTransaction.getHeight() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getConfirmationsDescription() {
|
||||||
|
int confirmations = getConfirmations();
|
||||||
|
if(confirmations == 0) {
|
||||||
|
return "Unconfirmed in mempool";
|
||||||
|
} else if(confirmations < BLOCKS_TO_FULLY_CONFIRM) {
|
||||||
|
return confirmations + " confirmation" + (confirmations == 1 ? "" : "s");
|
||||||
|
} else {
|
||||||
|
return BLOCKS_TO_FULLY_CONFIRM + "+ confirmations";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Entry> createChildEntries(Wallet wallet, Map<BlockTransactionHashIndex, KeyPurpose> incoming, Map<BlockTransactionHashIndex, KeyPurpose> outgoing) {
|
private static List<Entry> createChildEntries(Wallet wallet, Map<BlockTransactionHashIndex, KeyPurpose> incoming, Map<BlockTransactionHashIndex, KeyPurpose> outgoing) {
|
||||||
List<Entry> incomingOutputEntries = incoming.entrySet().stream().map(input -> new TransactionHashIndexEntry(wallet, input.getKey(), HashIndexEntry.Type.OUTPUT, input.getValue())).collect(Collectors.toList());
|
List<Entry> incomingOutputEntries = incoming.entrySet().stream().map(input -> new TransactionHashIndexEntry(wallet, input.getKey(), HashIndexEntry.Type.OUTPUT, input.getValue())).collect(Collectors.toList());
|
||||||
List<Entry> outgoingInputEntries = outgoing.entrySet().stream().map(output -> new TransactionHashIndexEntry(wallet, output.getKey(), HashIndexEntry.Type.INPUT, output.getValue())).collect(Collectors.toList());
|
List<Entry> outgoingInputEntries = outgoing.entrySet().stream().map(output -> new TransactionHashIndexEntry(wallet, output.getKey(), HashIndexEntry.Type.INPUT, output.getValue())).collect(Collectors.toList());
|
||||||
|
@ -116,7 +138,51 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@NotNull TransactionEntry other) {
|
public int compareTo(TransactionEntry other) {
|
||||||
return blockTransaction.compareTo(other.blockTransaction);
|
return blockTransaction.compareTo(other.blockTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the number of confirmations
|
||||||
|
*/
|
||||||
|
private IntegerProperty confirmations;
|
||||||
|
|
||||||
|
public final void setConfirmations(int value) {
|
||||||
|
if(confirmations != null || value != 0) {
|
||||||
|
confirmationsProperty().set(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getConfirmations() {
|
||||||
|
return confirmations == null ? 0 : confirmations.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final IntegerProperty confirmationsProperty() {
|
||||||
|
if(confirmations == null) {
|
||||||
|
confirmations = new IntegerPropertyBase(0) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBean() {
|
||||||
|
return TransactionEntry.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "confirmations";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return confirmations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void blockHeightChanged(WalletBlockHeightChangedEvent event) {
|
||||||
|
if(getWallet().equals(event.getWallet())) {
|
||||||
|
setConfirmations(calculateConfirmations());
|
||||||
|
|
||||||
|
if(!isFullyConfirming()) {
|
||||||
|
EventManager.get().unregister(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,12 @@ package com.sparrowwallet.sparrow.wallet;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.control.TransactionsTreeTable;
|
import com.sparrowwallet.sparrow.control.TransactionsTreeTable;
|
||||||
|
import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.WalletHistoryChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletHistoryChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.WalletNodesChangedEvent;
|
import com.sparrowwallet.sparrow.event.WalletNodesChangedEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
@ -39,4 +41,12 @@ public class TransactionsController extends WalletFormController implements Init
|
||||||
transactionsTable.updateHistory(event.getHistoryChangedNodes());
|
transactionsTable.updateHistory(event.getHistoryChangedNodes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: Remove
|
||||||
|
public void advanceBlock(MouseEvent event) {
|
||||||
|
Integer currentBlock = getWalletForm().getWallet().getStoredBlockHeight();
|
||||||
|
getWalletForm().getWallet().setStoredBlockHeight(currentBlock+1);
|
||||||
|
System.out.println("Advancing from " + currentBlock + " to " + getWalletForm().getWallet().getStoredBlockHeight());
|
||||||
|
EventManager.get().post(new WalletBlockHeightChangedEvent(getWalletForm().getWallet(), currentBlock+1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class WalletForm {
|
||||||
historyChangedNodes.addAll(getHistoryChangedNodes(previousWallet.getNode(KeyPurpose.CHANGE).getChildren(), wallet.getNode(KeyPurpose.CHANGE).getChildren()));
|
historyChangedNodes.addAll(getHistoryChangedNodes(previousWallet.getNode(KeyPurpose.CHANGE).getChildren(), wallet.getNode(KeyPurpose.CHANGE).getChildren()));
|
||||||
|
|
||||||
if(!historyChangedNodes.isEmpty()) {
|
if(!historyChangedNodes.isEmpty()) {
|
||||||
Platform.runLater(() -> EventManager.get().post(new WalletHistoryChangedEvent(wallet, historyChangedNodes)));
|
Platform.runLater(() -> EventManager.get().post(new WalletHistoryChangedEvent(wallet, blockHeight, historyChangedNodes)));
|
||||||
} else if(blockHeight != null && !blockHeight.equals(previousWallet.getStoredBlockHeight())) {
|
} else if(blockHeight != null && !blockHeight.equals(previousWallet.getStoredBlockHeight())) {
|
||||||
Platform.runLater(() -> EventManager.get().post(new WalletBlockHeightChangedEvent(wallet, blockHeight)));
|
Platform.runLater(() -> EventManager.get().post(new WalletBlockHeightChangedEvent(wallet, blockHeight)));
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,16 @@ public class WalletForm {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void walletChanged(WalletChangedEvent event) {
|
public void walletLabelChanged(WalletEntryLabelChangedEvent event) {
|
||||||
|
backgroundSaveWallet(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void walletBlockHeightChanged(WalletBlockHeightChangedEvent event) {
|
||||||
|
backgroundSaveWallet(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backgroundSaveWallet(WalletChangedEvent event) {
|
||||||
if(event.getWallet().equals(wallet)) {
|
if(event.getWallet().equals(wallet)) {
|
||||||
try {
|
try {
|
||||||
save();
|
save();
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<Insets left="25.0" right="25.0" top="15.0" bottom="25.0" />
|
<Insets left="25.0" right="25.0" top="15.0" bottom="25.0" />
|
||||||
</padding>
|
</padding>
|
||||||
<top>
|
<top>
|
||||||
<Label styleClass="transactions-treetable-label" text="Transactions:"/>
|
<Label styleClass="transactions-treetable-label" text="Transactions:" /> <!-- onMouseClicked="#advanceBlock" -->
|
||||||
</top>
|
</top>
|
||||||
<center>
|
<center>
|
||||||
<TransactionsTreeTable fx:id="transactionsTable" />
|
<TransactionsTreeTable fx:id="transactionsTable" />
|
||||||
|
|
|
@ -47,7 +47,11 @@
|
||||||
-fx-text-fill: #a0a1a7;
|
-fx-text-fill: #a0a1a7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-table-row-cell:selected .hashindex-row {
|
.transaction-row.confirming {
|
||||||
|
-fx-text-fill: #696c77;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-table-row-cell:selected .hashindex-row, .tree-table-row-cell:selected .transaction-row {
|
||||||
-fx-text-fill: white;
|
-fx-text-fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +67,27 @@
|
||||||
-fx-strikethrough: true;
|
-fx-strikethrough: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.amount-cell .confirmation-progress {
|
||||||
|
-fx-pref-width: 14;
|
||||||
|
-fx-padding: 0 8 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-progress-circle, .confirmation-progress-tick {
|
||||||
|
-fx-stroke: -fx-text-base-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-table-row-cell:selected .confirmation-progress-circle, .tree-table-row-cell:selected .confirmation-progress-tick {
|
||||||
|
-fx-stroke: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmation-progress-arc {
|
||||||
|
-fx-fill: -fx-text-base-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-table-row-cell:selected .confirmation-progress-arc {
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
.entry-cell .button {
|
.entry-cell .button {
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
-fx-pref-height: 18;
|
-fx-pref-height: 18;
|
||||||
|
@ -78,3 +103,7 @@
|
||||||
.entry-cell:hover .button .label .text {
|
.entry-cell:hover .button .label .text {
|
||||||
-fx-fill: -fx-text-base-color;
|
-fx-fill: -fx-text-base-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tree-table-row-cell:selected .entry-cell:hover .button .label .text {
|
||||||
|
-fx-fill: white;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue