-added basic setting page + code cleanup

This commit is contained in:
QcMrHyde 2025-05-24 00:53:14 -04:00
parent e6e90c4f88
commit 2f6984c4cc
11 changed files with 315 additions and 185 deletions

View file

@ -7,6 +7,7 @@ import com.sparrowwallet.sparrow.Mode;
import com.sparrowwallet.sparrow.Theme;
import com.sparrowwallet.sparrow.control.QRDensity;
import com.sparrowwallet.sparrow.control.WebcamResolution;
import com.sparrowwallet.sparrow.joinstr.VpnGateway;
import com.sparrowwallet.sparrow.net.*;
import com.sparrowwallet.sparrow.wallet.FeeRatesSelection;
import com.sparrowwallet.sparrow.wallet.OptimizationStrategy;
@ -82,6 +83,18 @@ public class Config {
private Double appWidth;
private Double appHeight;
// Joinstr settings
private VpnGateway vpnGateway;
private String nostrRelay;
private String nodeUsername;
private String nodePassword;
// ================
private static Config INSTANCE;
private static Gson getGson() {
@ -685,6 +698,41 @@ public class Config {
flush();
}
public VpnGateway getVpnGateway() {
return vpnGateway;
}
public void setVpnGateway(VpnGateway vpnGateway) {
this.vpnGateway = vpnGateway;
flush();
}
public String getNostrRelay() {
return nostrRelay;
}
public void setNostrRelay(String nostrRelay) {
this.nostrRelay = nostrRelay;
flush();
}
public String getNodeUsername() {
return nodeUsername;
}
public void setNodeUsername(String nodeUsername) {
this.nodeUsername = nodeUsername;
flush();
}
public String getNodePassword() {
return nodePassword;
}
public void setNodePassword(String nodePassword) {
this.nodePassword = nodePassword;
flush();
}
private synchronized void flush() {
Gson gson = getGson();
try {

View file

@ -22,6 +22,8 @@ public class JoinstrController extends JoinstrFormController {
private Stage stage;
protected String selectedWallet;
@FXML
private StackPane joinstrPane;
@ -49,7 +51,7 @@ public class JoinstrController extends JoinstrFormController {
if(joinstrDisplay.getUserData().equals(display)) {
existing = true;
joinstrDisplay.setViewOrder(0);
} else if(display != JoinstrDisplay.LOCK) {
} else {
joinstrDisplay.setViewOrder(1);
}
}
@ -68,17 +70,14 @@ public class JoinstrController extends JoinstrFormController {
joinstrDisplay.setUserData(display);
joinstrDisplay.setViewOrder(1);
JoinstrFormController controller = displayLoader.getController();
JoinstrForm joinstrForm = getJoinstrForm();
controller.setJoinstrForm(joinstrForm);
controller.initializeView();
joinstrPane.getChildren().add(joinstrDisplay);
}
else {
JoinstrFormController controller = displayLoader.getController();
controller.initializeView();
}
JoinstrFormController controller = displayLoader.getController();
JoinstrForm joinstrForm = getJoinstrForm();
controller.setJoinstrForm(joinstrForm);
controller.initializeView();
} catch (IOException e) {
throw new IllegalStateException("Can't find pane", e);
}

View file

@ -5,5 +5,5 @@ public enum JoinstrDisplay {
MY_POOLS,
OTHER_POOLS,
HISTORY,
LOCK
SETTINGS
}

View file

@ -4,8 +4,6 @@ import com.sparrowwallet.sparrow.joinstr.control.JoinstrInfoPane;
import com.sparrowwallet.sparrow.joinstr.control.JoinstrPoolList;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;

View file

@ -0,0 +1,151 @@
package com.sparrowwallet.sparrow.joinstr;
import com.sparrowwallet.sparrow.io.Config;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.PasswordField;
public class SettingsController extends JoinstrFormController {
@FXML
Label urlLabel;
@FXML
Label selectedWalletLabel;
@FXML
TextField usernameTextField;
@FXML
PasswordField passwordTextField;
@FXML
TextField nostrRelayTextField;
@FXML
TextField hostTextField;
@FXML
TextField locationTextField;
@FXML
TextField ipAddressTextField;
@FXML
TextField portTextField;
@FXML
TextField protocolTextField;
private VpnGateway vpnGateway;
@Override
public void initializeView() {
try {
// Node settings
urlLabel.setText(Config.get().getServer().getUrl());
selectedWalletLabel.setText(this.getJoinstrForm().getWallet().getName());
usernameTextField.setText(Config.get().getNodeUsername());
usernameTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
Config.get().setNodeUsername(usernameTextField.getText());
}
});
passwordTextField.setText(Config.get().getNodePassword());
passwordTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
Config.get().setNodePassword(passwordTextField.getText());
}
});
nostrRelayTextField.setText(Config.get().getNostrRelay());
nostrRelayTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
if(nostrRelayTextField.getText().isEmpty()) {
nostrRelayTextField.setText("wss://nostr.fmt.wiz.biz");
}
Config.get().setNostrRelay(nostrRelayTextField.getText());
}
});
// VPN Gateway settings
vpnGateway = Config.get().getVpnGateway();
if(vpnGateway == null)
vpnGateway = new VpnGateway();
hostTextField.setText(vpnGateway.getHost());
hostTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
vpnGateway.setHost(hostTextField.getText());
Config.get().setVpnGateway(vpnGateway);
}
});
locationTextField.setText(vpnGateway.getLocation());
locationTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
vpnGateway.setLocation(locationTextField.getText());
Config.get().setVpnGateway(vpnGateway);
}
});
ipAddressTextField.setText(vpnGateway.getIpAddress());
ipAddressTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
vpnGateway.setIpAddress(ipAddressTextField.getText());
Config.get().setVpnGateway(vpnGateway);
}
});
portTextField.setText(String.valueOf(vpnGateway.getPort()));
portTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
if (!newValue.matches("\\d{0,4}")) {
portTextField.setText(newValue.replaceAll("[^\\d]", ""));
}
if (newValue.length() > 4) {
portTextField.setText(newValue.substring(0, 4));
}
vpnGateway.setPort(Integer.getInteger(portTextField.getText()));
Config.get().setVpnGateway(vpnGateway);
}
});
protocolTextField.setText(vpnGateway.getProtocol());
protocolTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
vpnGateway.setProtocol(protocolTextField.getText());
Config.get().setVpnGateway(vpnGateway);
}
});
} catch(Exception e) {
if(e != null) {}
}
}
}

View file

@ -0,0 +1,38 @@
package com.sparrowwallet.sparrow.joinstr;
import java.io.Serializable;
public class VpnGateway implements Serializable {
private String host;
private String location;
private String ipAddress;
private int port;
private String protocol;
public VpnGateway() {}
public VpnGateway(String host, String location, String ipAddress, int port, String protocol) {
this.host = host;
this.location = location;
this.ipAddress = ipAddress;
this.port = port;
this.protocol = protocol;
}
public String getHost() { return host; };
public void setHost(String host) { this.host = host; };
public String getLocation() { return location; };
public void setLocation(String location) { this.location = location; };
public String getIpAddress() { return ipAddress; };
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; };
public int getPort() { return port; };
public void setPort(int port) { this.port = port; };
public String getProtocol() { return protocol; };
public void setProtocol(String protocol) { this.protocol = protocol; };
}

View file

@ -1,170 +0,0 @@
package com.sparrowwallet.sparrow.joinstr.control;
import com.sparrowwallet.sparrow.joinstr.JoinstrAction;
import com.sparrowwallet.sparrow.joinstr.JoinstrPool;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class JoinstrPoolListRow extends GridPane {
private JoinstrPool poolData;
/// Constructor for header
public JoinstrPoolListRow() {
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
GridPane.setHgrow(this, Priority.ALWAYS);
setColumnConstraints();
Label relayLabel = new Label("Relay");
GridPane.setRowIndex(relayLabel, 0);
GridPane.setColumnIndex(relayLabel, 0);
Label pubkeyLabel = new Label("Pubkey (Shortened)");
GridPane.setRowIndex(pubkeyLabel, 0);
GridPane.setColumnIndex(pubkeyLabel, 1);
Label denominationLabel = new Label("Denomination");
GridPane.setRowIndex(denominationLabel, 0);
GridPane.setColumnIndex(denominationLabel, 2);
Label peersLabel = new Label("Peers");
GridPane.setRowIndex(peersLabel, 0);
GridPane.setColumnIndex(peersLabel, 3);
Label timeoutLabel = new Label("Timeout");
GridPane.setRowIndex(timeoutLabel, 0);
GridPane.setColumnIndex(timeoutLabel, 4);
Label actionLabel = new Label("Action");
GridPane.setRowIndex(actionLabel, 0);
GridPane.setColumnIndex(actionLabel, 5);
getChildren().addAll(relayLabel, pubkeyLabel, denominationLabel, peersLabel, timeoutLabel, actionLabel);
getStyleClass().add("joinstr-list-header");
}
/// Constructor for data rows
public JoinstrPoolListRow(JoinstrPool poolData_, JoinstrAction action) {
poolData = poolData_;
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
GridPane.setHgrow(this, Priority.ALWAYS);
setColumnConstraints();
VBox relayVBox = new VBox();
Label relayLabel = new Label(poolData.getRelay());
relayLabel.getStyleClass().add("joinstr-list-item");
Label portLabel = new Label("Port: " + poolData.getPort().toString());
portLabel.getStyleClass().add("joinstr-list-item-subtitle");
relayVBox.getChildren().addAll(relayLabel, portLabel);
GridPane.setRowIndex(relayVBox, 0);
GridPane.setColumnIndex(relayVBox, 0);
VBox pubkeyVBox = new VBox();
Label pubkeyLabel = new Label(poolData.getPubkey());
pubkeyLabel.getStyleClass().add("joinstr-list-item");
Label pubkeyTypeLabel = new Label("type");
pubkeyTypeLabel.getStyleClass().add("joinstr-list-item-subtitle");
pubkeyVBox.getChildren().addAll(pubkeyLabel, pubkeyTypeLabel);
GridPane.setRowIndex(pubkeyVBox, 0);
GridPane.setColumnIndex(pubkeyVBox, 1);
VBox denominationVBox = new VBox();
Label denominationLabel = new Label(poolData.getDenomination().toString());
denominationLabel.getStyleClass().add("joinstr-list-item");
Label denominationMinLabel = new Label("Min: _____ BTC");
denominationMinLabel.getStyleClass().add("joinstr-list-item-subtitle");
denominationVBox.getChildren().addAll(denominationLabel, denominationMinLabel);
GridPane.setRowIndex(denominationVBox, 0);
GridPane.setColumnIndex(denominationVBox, 2);
VBox peersVBox = new VBox();
Label peersLabel = new Label("__/__");
peersLabel.getStyleClass().add("joinstr-list-item");
Label peersMinLabel = new Label("Min: -");
peersMinLabel.getStyleClass().add("joinstr-list-item-subtitle");
peersVBox.getChildren().addAll(peersLabel, peersMinLabel);
GridPane.setRowIndex(peersVBox, 0);
GridPane.setColumnIndex(peersVBox, 3);
VBox timeoutVBox = new VBox();
Label timeoutLabel = new Label("0");
timeoutLabel.getStyleClass().add("joinstr-list-item");
Label timeoutTypeLabel = new Label("mins");
timeoutTypeLabel.getStyleClass().add("joinstr-list-item-subtitle");
timeoutVBox.getChildren().addAll(timeoutLabel, timeoutTypeLabel);
GridPane.setRowIndex(timeoutVBox, 0);
GridPane.setColumnIndex(timeoutVBox, 4);
Button actionButton = new Button();
actionButton.getStyleClass().add("joinstr-list-action-button");
switch(action) {
case JOIN -> {
actionButton.setText("Join");
actionButton.setOnAction(event -> {
System.out.println("Join " + poolData.getRelay() + "!");
});
}
case REMOVE -> {
// For MY_POOLS & HISTORY
actionButton.setText("Remove");
actionButton.setOnAction(event -> {
System.out.println("Remove " + poolData.getRelay() + "!");
});
}
}
GridPane.setRowIndex(actionButton, 0);
GridPane.setColumnIndex(actionButton, 5);
getChildren().addAll( relayVBox, pubkeyVBox, denominationVBox, peersVBox, timeoutVBox, actionButton);
getStyleClass().add("joinstr-list-row");
}
private void setColumnConstraints() {
ColumnConstraints column1 = new ColumnConstraints();
column1.setPercentWidth(25);
ColumnConstraints column2 = new ColumnConstraints();
column2.setPercentWidth(25);
ColumnConstraints column3 = new ColumnConstraints();
column3.setPercentWidth(16);
ColumnConstraints column4 = new ColumnConstraints();
column4.setPercentWidth(10);
ColumnConstraints column5 = new ColumnConstraints();
column5.setPercentWidth(10);
ColumnConstraints column6 = new ColumnConstraints();
column6.setPercentWidth(14);
getColumnConstraints().addAll(column1, column2, column3, column4, column5, column6);
}
public void handleActionButton(ActionEvent e) {
if(e.getSource()!=null) {
System.out.println("Action!");
};
}
}

View file

@ -9,7 +9,7 @@
<Insets top="30" right="30" bottom="30" left="30"/>
</padding>
<center>
<VBox maxWidth="Infinity" HBox.hgrow="ALWAYS" fx:id="contentVBox">
<VBox maxWidth="Infinity" fx:id="contentVBox">
<GridPane maxWidth="Infinity" HBox.hgrow="ALWAYS" hgap="10.0" vgap="10.0">
<columnConstraints>
<ColumnConstraints percentWidth="80" />

View file

@ -36,6 +36,11 @@
<JoinstrDisplay fx:constant="HISTORY"/>
</userData>
</ToggleButton>
<ToggleButton VBox.vgrow="ALWAYS" text="Settings" contentDisplay="CENTER" styleClass="list-item" maxHeight="Infinity" toggleGroup="$joinstrMenu">
<userData>
<JoinstrDisplay fx:constant="SETTINGS"/>
</userData>
</ToggleButton>
</VBox>
</left>
<center>

View file

@ -9,7 +9,7 @@
<Insets top="30" right="30" bottom="30" left="30"/>
</padding>
<center>
<VBox maxWidth="Infinity" HBox.hgrow="ALWAYS" fx:id="contentVBox">
<VBox maxWidth="Infinity" fx:id="contentVBox">
<GridPane maxWidth="Infinity" HBox.hgrow="ALWAYS" hgap="10.0" vgap="10.0">
<columnConstraints>
<ColumnConstraints percentWidth="80" />

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<BorderPane stylesheets="@joinstr.css, @../wallet/wallet.css, @../general.css" styleClass="wallet-pane" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.sparrowwallet.sparrow.joinstr.SettingsController">
<padding>
<Insets top="30" right="30" bottom="30" left="30"/>
</padding>
<center>
<VBox maxWidth="Infinity" fx:id="contentVBox">
<Label styleClass="title">Settings</Label>
<GridPane maxWidth="Infinity" HBox.hgrow="ALWAYS" hgap="10.0" vgap="10.0">
<padding>
<Insets top="20" right="0" bottom="20" left="0"/>
</padding>
<columnConstraints>
<ColumnConstraints percentWidth="15" />
<ColumnConstraints percentWidth="85" />
</columnConstraints>
<children>
<Label GridPane.rowIndex="0" GridPane.columnIndex="0">Node URL:</Label>
<Label GridPane.rowIndex="0" GridPane.columnIndex="1" fx:id="urlLabel" />
<Label GridPane.rowIndex="1" GridPane.columnIndex="0">Selected Wallet:</Label>
<Label GridPane.rowIndex="1" GridPane.columnIndex="1" fx:id="selectedWalletLabel" />
<Label GridPane.rowIndex="2" GridPane.columnIndex="0">Username:</Label>
<TextField prefWidth="50" maxWidth="100" GridPane.rowIndex="2" GridPane.columnIndex="1" fx:id="usernameTextField" />
<Label GridPane.rowIndex="3" GridPane.columnIndex="0">Password:</Label>
<PasswordField prefWidth="50" maxWidth="100" GridPane.rowIndex="3" GridPane.columnIndex="1" fx:id="passwordTextField" />
<Label GridPane.rowIndex="4" GridPane.columnIndex="0">Nostr Relay:</Label>
<TextField prefWidth="100" maxWidth="150" GridPane.rowIndex="4" GridPane.columnIndex="1" fx:id="nostrRelayTextField">wss://nostr.fmt.wiz.biz</TextField>
<Label GridPane.rowIndex="6" GridPane.columnIndex="0" styleClass="sub-title">VPN Gateway</Label>
<Label GridPane.rowIndex="7" GridPane.columnIndex="0">Host:</Label>
<TextField prefWidth="100" maxWidth="150" GridPane.rowIndex="7" GridPane.columnIndex="1" fx:id="hostTextField" />
<Label GridPane.rowIndex="8" GridPane.columnIndex="0">Location:</Label>
<TextField prefWidth="100" maxWidth="150" GridPane.rowIndex="8" GridPane.columnIndex="1" fx:id="locationTextField" />
<Label GridPane.rowIndex="11" GridPane.columnIndex="0">Protocol:</Label>
<TextField prefWidth="100" maxWidth="150" GridPane.rowIndex="11" GridPane.columnIndex="1" fx:id="protocolTextField" />
<Label GridPane.rowIndex="9" GridPane.columnIndex="0">IP Address:</Label>
<TextField prefWidth="100" maxWidth="150" GridPane.rowIndex="9" GridPane.columnIndex="1" fx:id="ipAddressTextField" />
<Label GridPane.rowIndex="10" GridPane.columnIndex="0">Port:</Label>
<TextField prefWidth="25" maxWidth="50" GridPane.rowIndex="10" GridPane.columnIndex="1" fx:id="portTextField" />
</children>
</GridPane>
</VBox>
</center>
</BorderPane>