diff --git a/src/main/java/com/sparrowwallet/sparrow/control/HelpLabel.java b/src/main/java/com/sparrowwallet/sparrow/control/HelpLabel.java new file mode 100644 index 00000000..c579c2b4 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/control/HelpLabel.java @@ -0,0 +1,45 @@ +package com.sparrowwallet.sparrow.control; + +import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import org.controlsfx.glyphfont.Glyph; + +public class HelpLabel extends Label { + private final Tooltip tooltip; + + public HelpLabel() { + super("", getHelpGlyph()); + tooltip = new Tooltip(); + tooltip.textProperty().bind(helpTextProperty()); + setTooltip(tooltip); + getStyleClass().add("help-label"); + } + + private static Glyph getHelpGlyph() { + Glyph lockGlyph = new Glyph("Font Awesome 5 Free Solid", FontAwesome5.Glyph.QUESTION_CIRCLE); + lockGlyph.getStyleClass().add("help-icon"); + lockGlyph.setFontSize(12); + return lockGlyph; + } + + public final StringProperty helpTextProperty() { + if(helpText == null) { + helpText = new SimpleStringProperty(this, "helpText", ""); + } + + return helpText; + } + + private StringProperty helpText; + + public final void setHelpText(String value) { + helpTextProperty().setValue(value); + } + + public final String getHelpText() { + return helpText == null ? "" : helpText.getValue(); + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java index 5c2dcadf..c4e7a915 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java @@ -116,6 +116,15 @@ public class ElectrumServer { return client.createRequest().returnAs(BlockHeaderTip.class).method("blockchain.headers.subscribe").id(1).execute(); } + public static synchronized boolean isConnected() { + if(transport != null) { + TcpTransport tcpTransport = (TcpTransport)transport; + return tcpTransport.isConnected(); + } + + return false; + } + public static synchronized void closeActiveConnection() throws ServerException { try { if(transport != null) { @@ -572,6 +581,10 @@ public class ElectrumServer { public String hex; public BlockHeader getBlockHeader() { + if(hex == null) { + return null; + } + byte[] blockHeaderBytes = Utils.hexToBytes(hex); return new BlockHeader(blockHeaderBytes); } @@ -715,6 +728,10 @@ public class ElectrumServer { } } + public boolean isConnected() { + return socket != null && running; + } + protected Socket createSocket() throws IOException { return socketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT)); } @@ -832,11 +849,20 @@ public class ElectrumServer { public static class ConnectionService extends ScheduledService implements Thread.UncaughtExceptionHandler { private static final int FEE_RATES_PERIOD = 5 * 60 * 1000; + private final boolean subscribe; private boolean firstCall = true; private Thread reader; private Throwable lastReaderException; private long feeRatesRetrievedAt; + public ConnectionService() { + this(true); + } + + public ConnectionService(boolean subscribe) { + this.subscribe = subscribe; + } + @Override protected Task createTask() { return new Task<>() { @@ -853,7 +879,13 @@ public class ElectrumServer { List serverVersion = electrumServer.getServerVersion(); firstCall = false; - BlockHeaderTip tip = electrumServer.subscribeBlockHeaders(); + BlockHeaderTip tip; + if(subscribe) { + tip = electrumServer.subscribeBlockHeaders(); + } else { + tip = new BlockHeaderTip(); + } + String banner = electrumServer.getServerBanner(); Map blockTargetFeeRates = electrumServer.getFeeEstimates(SendController.TARGET_BLOCKS_RANGE); diff --git a/src/main/java/com/sparrowwallet/sparrow/preferences/GeneralPreferencesController.java b/src/main/java/com/sparrowwallet/sparrow/preferences/GeneralPreferencesController.java index 0b7f41c6..124ac4e5 100644 --- a/src/main/java/com/sparrowwallet/sparrow/preferences/GeneralPreferencesController.java +++ b/src/main/java/com/sparrowwallet/sparrow/preferences/GeneralPreferencesController.java @@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.preferences; import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.sparrow.EventManager; +import com.sparrowwallet.sparrow.control.UnlabeledToggleSwitch; import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent; import com.sparrowwallet.sparrow.event.FiatCurrencySelectedEvent; import com.sparrowwallet.sparrow.io.Config; @@ -24,6 +25,12 @@ public class GeneralPreferencesController extends PreferencesDetailController { @FXML private ComboBox exchangeSource; + @FXML + private UnlabeledToggleSwitch groupByAddress; + + @FXML + private UnlabeledToggleSwitch includeMempoolChange; + private final ChangeListener fiatCurrencyListener = new ChangeListener() { @Override public void changed(ObservableValue observable, Currency oldValue, Currency newValue) { @@ -58,6 +65,15 @@ public class GeneralPreferencesController extends PreferencesDetailController { }); updateCurrencies(exchangeSource.getSelectionModel().getSelectedItem()); + + groupByAddress.setSelected(config.isGroupByAddress()); + includeMempoolChange.setSelected(config.isIncludeMempoolChange()); + groupByAddress.selectedProperty().addListener((observableValue, oldValue, newValue) -> { + config.setGroupByAddress(newValue); + }); + includeMempoolChange.selectedProperty().addListener((observableValue, oldValue, newValue) -> { + config.setIncludeMempoolChange(newValue); + }); } private void updateCurrencies(ExchangeSource exchangeSource) { diff --git a/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java b/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java index df2cdf8e..b2814ddd 100644 --- a/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java +++ b/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java @@ -3,12 +3,13 @@ package com.sparrowwallet.sparrow.preferences; import com.google.common.net.HostAndPort; import com.sparrowwallet.sparrow.control.TextFieldValidator; import com.sparrowwallet.sparrow.control.UnlabeledToggleSwitch; +import com.sparrowwallet.sparrow.event.ConnectionEvent; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.io.Config; import com.sparrowwallet.sparrow.io.ElectrumServer; -import com.sparrowwallet.sparrow.io.ServerException; import javafx.application.Platform; import javafx.beans.value.ChangeListener; +import javafx.concurrent.WorkerStateEvent; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Control; @@ -17,6 +18,7 @@ import javafx.scene.control.TextField; import javafx.scene.paint.Color; import javafx.stage.FileChooser; import javafx.stage.Stage; +import javafx.util.Duration; import org.controlsfx.glyphfont.Glyph; import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationSupport; @@ -123,40 +125,31 @@ public class ServerPreferencesController extends PreferencesDetailController { }); testConnection.setOnAction(event -> { - try { - ElectrumServer.closeActiveConnection(); - } catch (ServerException e) { - testResults.setText("Failed to disconnect:\n" + (e.getCause() != null ? e.getCause().getMessage() : e.getMessage())); - } - - ElectrumServer.ServerVersionService serverVersionService = new ElectrumServer.ServerVersionService(); - serverVersionService.setOnSucceeded(successEvent -> { - List serverVersion = serverVersionService.getValue(); - testResults.setText("Connected to " + serverVersion.get(0) + " on protocol version " + serverVersion.get(1)); - testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.CHECK_CIRCLE, Color.rgb(80, 161, 79))); - - ElectrumServer.ServerBannerService serverBannerService = new ElectrumServer.ServerBannerService(); - serverBannerService.setOnSucceeded(bannerSuccessEvent -> { - testResults.setText(testResults.getText() + "\nServer Banner: " + serverBannerService.getValue()); - }); - serverBannerService.setOnFailed(bannerFailEvent -> { - testResults.setText(testResults.getText() + "\nServer Banner: None"); - }); - serverBannerService.start(); - }); - serverVersionService.setOnFailed(failEvent -> { - Throwable e = failEvent.getSource().getException(); - String reason = e.getCause() != null ? e.getCause().getMessage() : e.getMessage(); - if(e.getCause() != null && e.getCause() instanceof SSLHandshakeException) { - reason = "SSL Handshake Error\n" + reason; - } - - testResults.setText("Could not connect:\n\n" + reason); - testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.EXCLAMATION_CIRCLE, Color.rgb(202, 18, 67))); - }); testResults.setText("Connecting to " + config.getElectrumServer() + "..."); testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.ELLIPSIS_H, null)); - serverVersionService.start(); + + boolean existingConnection = ElectrumServer.isConnected(); + if(existingConnection) { + ElectrumServer.ServerBannerService serverBannerService = new ElectrumServer.ServerBannerService(); + serverBannerService.setOnSucceeded(successEvent -> { + showConnectionSuccess(null, serverBannerService.getValue()); + }); + serverBannerService.setOnFailed(this::showConnectionFailure); + serverBannerService.start(); + } else { + ElectrumServer.ConnectionService connectionService = new ElectrumServer.ConnectionService(false); + connectionService.setPeriod(Duration.minutes(1)); + connectionService.setOnSucceeded(successEvent -> { + ConnectionEvent connectionEvent = (ConnectionEvent)connectionService.getValue(); + showConnectionSuccess(connectionEvent.getServerVersion(), connectionEvent.getServerBanner()); + connectionService.cancel(); + }); + connectionService.setOnFailed(workerStateEvent -> { + showConnectionFailure(workerStateEvent); + connectionService.cancel(); + }); + connectionService.start(); + } }); String electrumServer = config.getElectrumServer(); @@ -201,6 +194,27 @@ public class ServerPreferencesController extends PreferencesDetailController { } } + private void showConnectionSuccess(List serverVersion, String serverBanner) { + testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.CHECK_CIRCLE, Color.rgb(80, 161, 79))); + if(serverVersion != null) { + testResults.setText("Connected to " + serverVersion.get(0) + " on protocol version " + serverVersion.get(1)); + } + if(serverBanner != null) { + testResults.setText(testResults.getText() + "\nServer Banner: " + serverBanner); + } + } + + private void showConnectionFailure(WorkerStateEvent failEvent) { + Throwable e = failEvent.getSource().getException(); + String reason = e.getCause() != null ? e.getCause().getMessage() : e.getMessage(); + if(e.getCause() != null && e.getCause() instanceof SSLHandshakeException) { + reason = "SSL Handshake Error\n" + reason; + } + + testResults.setText("Could not connect:\n\n" + reason); + testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.EXCLAMATION_CIRCLE, Color.rgb(202, 18, 67))); + } + private void setupValidation() { validationSupport.registerValidator(host, Validator.combine( (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid host name", getHost(newValue) == null) diff --git a/src/main/resources/com/sparrowwallet/sparrow/general.css b/src/main/resources/com/sparrowwallet/sparrow/general.css index 7e56f45f..89f533b4 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/general.css +++ b/src/main/resources/com/sparrowwallet/sparrow/general.css @@ -5,6 +5,11 @@ -fx-padding: 5 0 5 0; } +.form .wideLabelFieldSet.fieldset:horizontal .label-container { + -fx-pref-width: 160px; + -fx-pref-height: 25px; +} + .form .fieldset:horizontal .label-container { -fx-pref-width: 110px; -fx-pref-height: 25px; @@ -112,4 +117,8 @@ .copyable-text-field .copy-button:pressed > .graphic { -fx-background-color: #116a8d; +} + +.help-label { + -fx-padding: 0 0 0 10; } \ No newline at end of file diff --git a/src/main/resources/com/sparrowwallet/sparrow/preferences/general.fxml b/src/main/resources/com/sparrowwallet/sparrow/preferences/general.fxml index f5312970..0aa0b020 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/preferences/general.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/preferences/general.fxml @@ -13,6 +13,9 @@ + + + @@ -25,8 +28,8 @@
-
- +
+ @@ -36,13 +39,14 @@ +
-
+
- + @@ -54,5 +58,15 @@
+
+ + + + + + + + +