mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
preferences update
This commit is contained in:
parent
3cee45223e
commit
bd721be1a2
6 changed files with 168 additions and 38 deletions
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<FeeRatesUpdatedEvent> 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<FeeRatesUpdatedEvent> createTask() {
|
||||
return new Task<>() {
|
||||
|
@ -853,7 +879,13 @@ public class ElectrumServer {
|
|||
List<String> 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<Integer, Double> blockTargetFeeRates = electrumServer.getFeeEstimates(SendController.TARGET_BLOCKS_RANGE);
|
||||
|
|
|
@ -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> exchangeSource;
|
||||
|
||||
@FXML
|
||||
private UnlabeledToggleSwitch groupByAddress;
|
||||
|
||||
@FXML
|
||||
private UnlabeledToggleSwitch includeMempoolChange;
|
||||
|
||||
private final ChangeListener<Currency> fiatCurrencyListener = new ChangeListener<Currency>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Currency> 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) {
|
||||
|
|
|
@ -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<String> 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<String> 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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -13,6 +13,9 @@
|
|||
<?import javafx.geometry.Insets?>
|
||||
<?import com.sparrowwallet.drongo.BitcoinUnit?>
|
||||
<?import com.sparrowwallet.sparrow.io.ExchangeSource?>
|
||||
<?import com.sparrowwallet.sparrow.control.UnlabeledToggleSwitch?>
|
||||
<?import com.sparrowwallet.sparrow.control.HelpLabel?>
|
||||
|
||||
<GridPane hgap="10.0" vgap="10.0" stylesheets="@preferences.css, @../general.css" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.sparrowwallet.sparrow.preferences.GeneralPreferencesController">
|
||||
<padding>
|
||||
<Insets left="25.0" right="25.0" top="25.0" />
|
||||
|
@ -25,8 +28,8 @@
|
|||
</rowConstraints>
|
||||
|
||||
<Form GridPane.columnIndex="0" GridPane.rowIndex="0">
|
||||
<Fieldset inputGrow="SOMETIMES" text="Bitcoin">
|
||||
<Field text="Unit:">
|
||||
<Fieldset inputGrow="SOMETIMES" text="Bitcoin" styleClass="wideLabelFieldSet">
|
||||
<Field text="Display unit:">
|
||||
<ComboBox fx:id="bitcoinUnit">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
|
@ -36,13 +39,14 @@
|
|||
</FXCollections>
|
||||
</items>
|
||||
</ComboBox>
|
||||
<HelpLabel helpText="Display unit for bitcoin amounts. Auto displays amounts over 1 BTC in BTC, and amounts under in satoshis"/>
|
||||
</Field>
|
||||
</Fieldset>
|
||||
<Fieldset inputGrow="SOMETIMES" text="Fiat">
|
||||
<Fieldset inputGrow="SOMETIMES" text="Fiat" styleClass="wideLabelFieldSet">
|
||||
<Field text="Currency:">
|
||||
<ComboBox fx:id="fiatCurrency" />
|
||||
</Field>
|
||||
<Field text="Source:">
|
||||
<Field text="Exchange rate source:">
|
||||
<ComboBox fx:id="exchangeSource">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
|
@ -54,5 +58,15 @@
|
|||
</ComboBox>
|
||||
</Field>
|
||||
</Fieldset>
|
||||
<Fieldset inputGrow="SOMETIMES" text="Coin Selection" styleClass="wideLabelFieldSet">
|
||||
<Field text="Group by address:">
|
||||
<UnlabeledToggleSwitch fx:id="groupByAddress" />
|
||||
<HelpLabel helpText="Group UTXOs by address when sending to improve privacy by only sending from an address once"/>
|
||||
</Field>
|
||||
<Field text="Include mempool change:">
|
||||
<UnlabeledToggleSwitch fx:id="includeMempoolChange" />
|
||||
<HelpLabel helpText="Allow a wallet to spend UTXOs that are still in the mempool where all their inputs are from that wallet"/>
|
||||
</Field>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
</GridPane>
|
||||
|
|
Loading…
Reference in a new issue