mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
configure a block explorer url, and open a txid in the configured block explorer
This commit is contained in:
parent
90a9030ecb
commit
dfe1f16495
10 changed files with 185 additions and 34 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit d0da764aadfc9edc2a7fb35540eca0ee4c20ba0f
|
||||
Subproject commit 0f78efc373e99cf48a749ced25cd112ca083b88f
|
|
@ -811,6 +811,20 @@ public class AppServices {
|
|||
}
|
||||
}
|
||||
|
||||
public static void openBlockExplorer(String txid) {
|
||||
Server blockExplorer = Config.get().getBlockExplorer() == null ? BlockExplorer.MEMPOOL_SPACE.getServer() : Config.get().getBlockExplorer();
|
||||
String url = blockExplorer.getUrl();
|
||||
if(url.contains("{0}")) {
|
||||
url = url.replace("{0}", txid);
|
||||
} else {
|
||||
if(Network.get() != Network.MAINNET) {
|
||||
url += "/" + Network.get().getName();
|
||||
}
|
||||
url += "/tx/" + txid;
|
||||
}
|
||||
AppServices.get().getApplication().getHostServices().showDocument(url);
|
||||
}
|
||||
|
||||
static void parseFileUriArguments(List<String> fileUriArguments) {
|
||||
for(String fileUri : fileUriArguments) {
|
||||
try {
|
||||
|
|
|
@ -537,6 +537,13 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
|
|||
getItems().add(createCpfp);
|
||||
}
|
||||
|
||||
MenuItem openBlockExplorer = new MenuItem("Open in Block Explorer");
|
||||
openBlockExplorer.setOnAction(AE -> {
|
||||
hide();
|
||||
AppServices.openBlockExplorer(blockTransaction.getHashAsString());
|
||||
});
|
||||
getItems().add(openBlockExplorer);
|
||||
|
||||
MenuItem copyTxid = new MenuItem("Copy Transaction ID");
|
||||
copyTxid.setOnAction(AE -> {
|
||||
hide();
|
||||
|
@ -558,6 +565,12 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
|
|||
EventManager.get().post(new ViewTransactionEvent(this.getOwnerWindow(), blockTransaction));
|
||||
});
|
||||
|
||||
MenuItem openBlockExplorer = new MenuItem("Open in Block Explorer");
|
||||
openBlockExplorer.setOnAction(AE -> {
|
||||
hide();
|
||||
AppServices.openBlockExplorer(blockTransaction.getHashAsString());
|
||||
});
|
||||
|
||||
MenuItem copyDate = new MenuItem("Copy Date");
|
||||
copyDate.setOnAction(AE -> {
|
||||
hide();
|
||||
|
@ -582,7 +595,7 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
|
|||
Clipboard.getSystemClipboard().setContent(content);
|
||||
});
|
||||
|
||||
getItems().addAll(viewTransaction, copyDate, copyTxid, copyHeight);
|
||||
getItems().addAll(viewTransaction, openBlockExplorer, copyDate, copyTxid, copyHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ public class FontAwesome5 extends GlyphFont {
|
|||
TOGGLE_OFF('\uf204'),
|
||||
TOGGLE_ON('\uf205'),
|
||||
TOOLS('\uf7d9'),
|
||||
UP_RIGHT_FROM_SQUARE('\uf35d'),
|
||||
UNDO('\uf0e2'),
|
||||
USER('\uf007'),
|
||||
USER_FRIENDS('\uf500'),
|
||||
|
|
|
@ -30,6 +30,7 @@ public class Config {
|
|||
private Mode mode;
|
||||
private BitcoinUnit bitcoinUnit;
|
||||
private UnitFormat unitFormat;
|
||||
private Server blockExplorer;
|
||||
private FeeRatesSource feeRatesSource;
|
||||
private FeeRatesSelection feeRatesSelection;
|
||||
private OptimizationStrategy sendOptimizationStrategy;
|
||||
|
@ -150,6 +151,15 @@ public class Config {
|
|||
flush();
|
||||
}
|
||||
|
||||
public Server getBlockExplorer() {
|
||||
return blockExplorer;
|
||||
}
|
||||
|
||||
public void setBlockExplorer(Server blockExplorer) {
|
||||
this.blockExplorer = blockExplorer;
|
||||
flush();
|
||||
}
|
||||
|
||||
public FeeRatesSource getFeeRatesSource() {
|
||||
return feeRatesSource;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.sparrowwallet.sparrow.net;
|
||||
|
||||
import com.sparrowwallet.sparrow.io.Server;
|
||||
|
||||
public enum BlockExplorer {
|
||||
MEMPOOL_SPACE("https://mempool.space"),
|
||||
BLOCKSTREAM_INFO("https://blockstream.info");
|
||||
|
||||
private final Server server;
|
||||
|
||||
BlockExplorer(String url) {
|
||||
this.server = new Server(url);
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
}
|
|
@ -1,32 +1,44 @@
|
|||
package com.sparrowwallet.sparrow.preferences;
|
||||
|
||||
import com.sparrowwallet.drongo.BitcoinUnit;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.control.UnlabeledToggleSwitch;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.Config;
|
||||
import com.sparrowwallet.sparrow.io.Server;
|
||||
import com.sparrowwallet.sparrow.net.BlockExplorer;
|
||||
import com.sparrowwallet.sparrow.net.ExchangeSource;
|
||||
import com.sparrowwallet.sparrow.net.FeeRatesSource;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import javafx.util.StringConverter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GeneralPreferencesController extends PreferencesDetailController {
|
||||
private static final Logger log = LoggerFactory.getLogger(GeneralPreferencesController.class);
|
||||
|
||||
@FXML
|
||||
private ComboBox<BitcoinUnit> bitcoinUnit;
|
||||
private static final Server CUSTOM_BLOCK_EXPLORER = new Server("http://custom.block.explorer");
|
||||
|
||||
@FXML
|
||||
private ComboBox<FeeRatesSource> feeRatesSource;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Server> blockExplorers;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Currency> fiatCurrency;
|
||||
|
||||
|
@ -63,17 +75,6 @@ public class GeneralPreferencesController extends PreferencesDetailController {
|
|||
|
||||
@Override
|
||||
public void initializeView(Config config) {
|
||||
if(config.getBitcoinUnit() != null) {
|
||||
bitcoinUnit.setValue(config.getBitcoinUnit());
|
||||
} else {
|
||||
bitcoinUnit.setValue(BitcoinUnit.AUTO);
|
||||
}
|
||||
|
||||
bitcoinUnit.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
config.setBitcoinUnit(newValue);
|
||||
EventManager.get().post(new BitcoinUnitChangedEvent(newValue));
|
||||
});
|
||||
|
||||
if(config.getFeeRatesSource() != null) {
|
||||
feeRatesSource.setValue(config.getFeeRatesSource());
|
||||
} else {
|
||||
|
@ -86,6 +87,62 @@ public class GeneralPreferencesController extends PreferencesDetailController {
|
|||
EventManager.get().post(new FeeRatesSourceChangedEvent(newValue));
|
||||
});
|
||||
|
||||
blockExplorers.setItems(getBlockExplorerList());
|
||||
blockExplorers.setConverter(new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(Server server) {
|
||||
if(server == null) {
|
||||
return "None Available";
|
||||
}
|
||||
|
||||
if(server == CUSTOM_BLOCK_EXPLORER) {
|
||||
return "Custom...";
|
||||
}
|
||||
|
||||
return server.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
blockExplorers.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(newValue != null) {
|
||||
if(newValue == CUSTOM_BLOCK_EXPLORER) {
|
||||
TextInputDialog textInputDialog = new TextInputDialog();
|
||||
textInputDialog.setTitle("Enter Block Explorer URL");
|
||||
textInputDialog.setHeaderText("Enter the URL of the block explorer.\n\nIf present, the characters {0} will be replaced with the txid.\nFor example, https://localhost or https://localhost/tx/{0}\n");
|
||||
textInputDialog.getEditor().setPromptText("https://localhost");
|
||||
Optional<String> optUrl = textInputDialog.showAndWait();
|
||||
if(optUrl.isPresent() && !optUrl.get().isEmpty()) {
|
||||
try {
|
||||
Server server = getBlockExplorer(optUrl.get());
|
||||
config.setBlockExplorer(server);
|
||||
Platform.runLater(() -> {
|
||||
blockExplorers.getSelectionModel().select(-1);
|
||||
blockExplorers.setItems(getBlockExplorerList());
|
||||
blockExplorers.setValue(Config.get().getBlockExplorer());
|
||||
});
|
||||
} catch(Exception e) {
|
||||
AppServices.showErrorDialog("Invalid URL", "The URL " + optUrl.get() + " is not valid.");
|
||||
blockExplorers.setValue(oldValue);
|
||||
}
|
||||
} else {
|
||||
blockExplorers.setValue(oldValue);
|
||||
}
|
||||
} else {
|
||||
Config.get().setBlockExplorer(newValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(config.getBlockExplorer() != null) {
|
||||
blockExplorers.setValue(config.getBlockExplorer());
|
||||
} else {
|
||||
blockExplorers.getSelectionModel().select(0);
|
||||
}
|
||||
|
||||
if(config.getExchangeSource() != null) {
|
||||
exchangeSource.setValue(config.getExchangeSource());
|
||||
} else {
|
||||
|
@ -134,6 +191,23 @@ public class GeneralPreferencesController extends PreferencesDetailController {
|
|||
});
|
||||
}
|
||||
|
||||
private static Server getBlockExplorer(String serverUrl) {
|
||||
String url = serverUrl.trim();
|
||||
if(url.endsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
return new Server(url);
|
||||
}
|
||||
|
||||
private ObservableList<Server> getBlockExplorerList() {
|
||||
List<Server> servers = Arrays.stream(BlockExplorer.values()).map(BlockExplorer::getServer).collect(Collectors.toList());
|
||||
if(Config.get().getBlockExplorer() != null && !servers.contains(Config.get().getBlockExplorer())) {
|
||||
servers.add(Config.get().getBlockExplorer());
|
||||
}
|
||||
servers.add(CUSTOM_BLOCK_EXPLORER);
|
||||
return FXCollections.observableList(servers);
|
||||
}
|
||||
|
||||
private void updateCurrencies(ExchangeSource exchangeSource) {
|
||||
ExchangeSource.CurrenciesService currenciesService = new ExchangeSource.CurrenciesService(exchangeSource);
|
||||
currenciesService.setOnSucceeded(event -> {
|
||||
|
|
|
@ -78,6 +78,12 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
@FXML
|
||||
private IdLabel id;
|
||||
|
||||
@FXML
|
||||
private ToggleButton copyTxid;
|
||||
|
||||
@FXML
|
||||
private ToggleButton openBlockExplorer;
|
||||
|
||||
@FXML
|
||||
private TransactionDiagram transactionDiagram;
|
||||
|
||||
|
@ -806,11 +812,13 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
addStyleClass(size, UNFINALIZED_TXID_CLASS);
|
||||
addStyleClass(virtualSize, UNFINALIZED_TXID_CLASS);
|
||||
addStyleClass(feeRate, UNFINALIZED_TXID_CLASS);
|
||||
openBlockExplorer.setDisable(true);
|
||||
} else {
|
||||
id.getStyleClass().remove(UNFINALIZED_TXID_CLASS);
|
||||
size.getStyleClass().remove(UNFINALIZED_TXID_CLASS);
|
||||
virtualSize.getStyleClass().remove(UNFINALIZED_TXID_CLASS);
|
||||
feeRate.getStyleClass().remove(UNFINALIZED_TXID_CLASS);
|
||||
openBlockExplorer.setDisable(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -821,11 +829,18 @@ public class HeadersController extends TransactionFormController implements Init
|
|||
}
|
||||
|
||||
public void copyId(ActionEvent event) {
|
||||
copyTxid.setSelected(false);
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(headersForm.getTransaction().calculateTxId(false).toString());
|
||||
Clipboard.getSystemClipboard().setContent(content);
|
||||
}
|
||||
|
||||
public void openInBlockExplorer(ActionEvent event) {
|
||||
openBlockExplorer.setSelected(false);
|
||||
String txid = headersForm.getTransaction().calculateTxId(false).toString();
|
||||
AppServices.openBlockExplorer(txid);
|
||||
}
|
||||
|
||||
public void setLocktimeToCurrentHeight(ActionEvent event) {
|
||||
if(AppServices.getCurrentBlockHeight() != null && locktimeBlock.isEditable()) {
|
||||
locktimeBlock.getValueFactory().setValue(AppServices.getCurrentBlockHeight());
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
<?import tornadofx.control.Field?>
|
||||
<?import javafx.collections.FXCollections?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import com.sparrowwallet.drongo.BitcoinUnit?>
|
||||
<?import com.sparrowwallet.sparrow.net.ExchangeSource?>
|
||||
<?import com.sparrowwallet.sparrow.control.UnlabeledToggleSwitch?>
|
||||
<?import com.sparrowwallet.sparrow.control.HelpLabel?>
|
||||
|
@ -30,18 +29,6 @@
|
|||
|
||||
<Form GridPane.columnIndex="0" GridPane.rowIndex="0">
|
||||
<Fieldset inputGrow="SOMETIMES" text="Bitcoin" styleClass="wideLabelFieldSet">
|
||||
<Field text="Display unit:">
|
||||
<ComboBox fx:id="bitcoinUnit">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
<BitcoinUnit fx:constant="AUTO" />
|
||||
<BitcoinUnit fx:constant="BTC" />
|
||||
<BitcoinUnit fx:constant="SATOSHIS" />
|
||||
</FXCollections>
|
||||
</items>
|
||||
</ComboBox>
|
||||
<HelpLabel helpText="Display unit for bitcoin amounts.\nAuto displays amounts over 1 BTC in BTC, and amounts under that in satoshis."/>
|
||||
</Field>
|
||||
<Field text="Fee rates source:">
|
||||
<ComboBox fx:id="feeRatesSource">
|
||||
<items>
|
||||
|
@ -54,6 +41,10 @@
|
|||
</ComboBox>
|
||||
<HelpLabel helpText="Recommended fee rates can be sourced from your connected Electrum server, or an external source."/>
|
||||
</Field>
|
||||
<Field text="Block explorer:">
|
||||
<ComboBox fx:id="blockExplorers" />
|
||||
<HelpLabel helpText="An external block explorer can be configured to open a transaction from any transaction tab."/>
|
||||
</Field>
|
||||
</Fieldset>
|
||||
<Fieldset inputGrow="SOMETIMES" text="Fiat" styleClass="wideLabelFieldSet">
|
||||
<Field text="Currency:">
|
||||
|
|
|
@ -45,11 +45,26 @@
|
|||
<Fieldset text="Transaction" inputGrow="SOMETIMES" wrapWidth="620">
|
||||
<Field text="Txid:" styleClass="label-button">
|
||||
<IdLabel fx:id="id"/>
|
||||
<Button maxWidth="25" minWidth="-Infinity" prefWidth="30" text="Cy" onAction="#copyId">
|
||||
<SegmentedButton>
|
||||
<buttons>
|
||||
<ToggleButton fx:id="copyTxid" onAction="#copyId">
|
||||
<graphic>
|
||||
<Glyph fontFamily="FontAwesome" icon="COPY" prefWidth="15" />
|
||||
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="COPY" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy the transaction id to the clipboard"/>
|
||||
</tooltip>
|
||||
</ToggleButton>
|
||||
<ToggleButton fx:id="openBlockExplorer" onAction="#openInBlockExplorer">
|
||||
<graphic>
|
||||
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="UP_RIGHT_FROM_SQUARE" />
|
||||
</graphic>
|
||||
<tooltip>
|
||||
<Tooltip text="Open transaction in the configured block explorer"/>
|
||||
</tooltip>
|
||||
</ToggleButton>
|
||||
</buttons>
|
||||
</SegmentedButton>
|
||||
</Field>
|
||||
</Fieldset>
|
||||
</Form>
|
||||
|
|
Loading…
Reference in a new issue