Browse Source

all walletconfig for wallet scope configuration variables

master
Craig Raw 2 years ago
parent
commit
cc961b4eeb
  1. 5
      build.gradle
  2. 2
      drongo
  3. 80
      src/main/java/com/sparrowwallet/sparrow/AppController.java
  4. 3
      src/main/java/com/sparrowwallet/sparrow/SparrowDesktop.java
  5. 3
      src/main/java/com/sparrowwallet/sparrow/control/PayNymAvatar.java
  6. 167
      src/main/java/com/sparrowwallet/sparrow/control/WalletIcon.java
  7. 22
      src/main/java/com/sparrowwallet/sparrow/event/PayNymImageLoadedEvent.java
  8. 9
      src/main/java/com/sparrowwallet/sparrow/event/WalletConfigChangedEvent.java
  9. 56
      src/main/java/com/sparrowwallet/sparrow/io/ImageUtils.java
  10. 14
      src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java
  11. 36
      src/main/java/com/sparrowwallet/sparrow/io/db/WalletConfigDao.java
  12. 21
      src/main/java/com/sparrowwallet/sparrow/io/db/WalletConfigMapper.java
  13. 10
      src/main/java/com/sparrowwallet/sparrow/io/db/WalletDao.java
  14. 29
      src/main/java/com/sparrowwallet/sparrow/paynym/PayNymController.java
  15. 8
      src/main/java/com/sparrowwallet/sparrow/soroban/CounterpartyController.java
  16. 6
      src/main/java/com/sparrowwallet/sparrow/soroban/InitiatorController.java
  17. 28
      src/main/java/com/sparrowwallet/sparrow/soroban/SorobanController.java
  18. 18
      src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java
  19. 23
      src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java
  20. 1
      src/main/java/module-info.java
  21. 1
      src/main/resources/com/sparrowwallet/sparrow/sql/V8__WalletConfig.sql
  22. BIN
      src/main/resources/image/bitbox02-icon.png
  23. BIN
      src/main/resources/image/bitbox02-icon@2x.png
  24. BIN
      src/main/resources/image/coldcard-icon.png
  25. BIN
      src/main/resources/image/coldcard-icon@2x.png
  26. BIN
      src/main/resources/image/ledger-icon.png
  27. BIN
      src/main/resources/image/ledger-icon@2x.png
  28. BIN
      src/main/resources/image/passport-icon.png
  29. BIN
      src/main/resources/image/passport-icon@2x.png
  30. BIN
      src/main/resources/image/seedsigner-icon.png
  31. BIN
      src/main/resources/image/seedsigner-icon@2x.png
  32. BIN
      src/main/resources/image/trezor-icon.png
  33. BIN
      src/main/resources/image/trezor-icon@2x.png

5
build.gradle

@ -110,6 +110,7 @@ dependencies {
implementation('net.sourceforge.streamsupport:streamsupport:1.7.0')
implementation('com.github.librepdf:openpdf:1.3.27')
implementation('com.googlecode.lanterna:lanterna:3.1.1')
implementation('net.coobird:thumbnailator:0.4.18')
testImplementation('junit:junit:4.12')
}
@ -579,6 +580,10 @@ extraJavaModuleInfo {
module('jcip-annotations-1.0.jar', 'net.jcip.annotations', '1.0') {
exports('net.jcip.annotations')
}
module('thumbnailator-0.4.18.jar', 'net.coobird.thumbnailator', '0.4.18') {
exports('net.coobird.thumbnailator')
requires('java.desktop')
}
module("netlayer-jpms-${osName}${targetName}-0.6.8.jar", 'netlayer.jpms', '0.6.8') {
exports('org.berndpruenster.netlayer.tor')
requires('com.github.ravn.jsocks')

2
drongo

@ -1 +1 @@
Subproject commit 7c34ec7c3b818634b7819f1b27a5f9c57f5554c8
Subproject commit fa18ec9d458bb17221fb01b6be9f4eceb354a156

80
src/main/java/com/sparrowwallet/sparrow/AppController.java

@ -1463,11 +1463,10 @@ public class AppController implements Initializable {
wallet.setName(name);
}
Tab tab = new Tab("");
Glyph glyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.WALLET);
glyph.setFontSize(10.0);
glyph.setOpacity(TAB_LABEL_GRAPHIC_OPACITY_ACTIVE);
WalletIcon walletIcon = new WalletIcon(storage, wallet);
walletIcon.setOpacity(TAB_LABEL_GRAPHIC_OPACITY_ACTIVE);
Label tabLabel = new Label(name);
tabLabel.setGraphic(glyph);
tabLabel.setGraphic(walletIcon);
tabLabel.setGraphicTextGap(5.0);
tab.setGraphic(tabLabel);
tab.setClosable(true);
@ -1856,17 +1855,61 @@ public class AppController implements Initializable {
contextMenu.getItems().addAll(new SeparatorMenuItem(), close, closeOthers, closeAll);
if(tab.getUserData() instanceof WalletTabData) {
if(tab.getUserData() instanceof WalletTabData walletTabData) {
Menu walletIcon = new Menu("Wallet Icon");
MenuItem custom = new MenuItem("Custom...");
custom.setOnAction(event -> {
setCustomIcon(walletTabData.getWallet());
});
MenuItem reset = new MenuItem("Reset");
reset.setOnAction(event -> {
resetIcon(walletTabData.getWalletForm());
});
walletIcon.getItems().addAll(custom, reset);
MenuItem delete = new MenuItem("Delete...");
delete.setOnAction(event -> {
deleteWallet(getSelectedWalletForm());
deleteWallet(walletTabData.getWalletForm());
});
contextMenu.getItems().addAll(new SeparatorMenuItem(), delete);
contextMenu.getItems().addAll(new SeparatorMenuItem(), walletIcon, delete);
}
return contextMenu;
}
private void setCustomIcon(Wallet wallet) {
Stage window = new Stage();
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Open Image");
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("All Files", org.controlsfx.tools.Platform.getCurrent().equals(org.controlsfx.tools.Platform.UNIX) ? "*" : "*.*"),
new FileChooser.ExtensionFilter("Images", "*.png", "*.jpg", "*.jpeg", "*.gif")
);
AppServices.moveToActiveWindowScreen(window, 800, 450);
File file = fileChooser.showOpenDialog(window);
if(file != null) {
try {
byte[] iconData = ImageUtils.resize(file, WalletIcon.SAVE_WIDTH, WalletIcon.SAVE_HEIGHT);
WalletConfig walletConfig = wallet.getMasterWalletConfig();
walletConfig.setIconData(iconData, true);
EventManager.get().post(new WalletConfigChangedEvent(wallet));
} catch(Exception e) {
log.error("Error creating custom wallet icon", e);
showErrorDialog("Error creating custom wallet icon", e.getMessage());
}
}
}
private void resetIcon(WalletForm walletForm) {
Wallet masterWallet = walletForm.getMasterWallet();
if(masterWallet.getWalletConfig() != null && masterWallet.getWalletConfig().isUserIcon()) {
masterWallet.getWalletConfig().setIconData(null, false);
EventManager.get().post(new WalletConfigChangedEvent(masterWallet));
}
}
private void deleteWallet(WalletForm selectedWalletForm) {
Optional<ButtonType> optButtonType = AppServices.showWarningDialog("Delete " + selectedWalletForm.getWallet().getMasterName() + "?", "The wallet file and any backups will be deleted. Are you sure?", ButtonType.NO, ButtonType.YES);
if(optButtonType.isPresent() && optButtonType.get() == ButtonType.YES) {
@ -2071,8 +2114,8 @@ public class AppController implements Initializable {
private void tabLabelAddFailure(Tab tab) {
Label tabLabel = (Label)tab.getGraphic();
if(!tabLabel.getStyleClass().contains("failure")) {
tabLabel.getGraphic().getStyleClass().add("failure");
WalletIcon walletIcon = (WalletIcon)tabLabel.getGraphic();
if(walletIcon.addFailure()) {
tabLabel.setTooltip(new Tooltip("Error loading transaction history from server"));
}
}
@ -2105,7 +2148,8 @@ public class AppController implements Initializable {
private void tabLabelRemoveFailure(Tab tab) {
Label tabLabel = (Label)tab.getGraphic();
tabLabel.getGraphic().getStyleClass().remove("failure");
WalletIcon walletIcon = (WalletIcon)tabLabel.getGraphic();
walletIcon.removeFailure();
tabLabel.setTooltip(null);
}
@ -2222,6 +2266,9 @@ public class AppController implements Initializable {
Tab masterTab = subTabs.getTabs().stream().filter(tab -> ((WalletTabData)tab.getUserData()).getWallet().isMasterWallet()).findFirst().orElse(subTabs.getTabs().get(0));
Label masterLabel = (Label)masterTab.getGraphic();
masterLabel.setText(event.getWallet().getLabel() != null ? event.getWallet().getLabel() : event.getWallet().getAutomaticName());
Label tabLabel = (Label)walletTab.getGraphic();
WalletIcon walletIcon = (WalletIcon)tabLabel.getGraphic();
walletIcon.setWallet(event.getWallet());
}
}
}
@ -2749,4 +2796,17 @@ public class AppController implements Initializable {
lockAllWallets.setDisable(false);
}
}
@Subscribe
public void walletConfigChanged(WalletConfigChangedEvent event) {
for(Tab tab : tabs.getTabs()) {
if(tab.getUserData() instanceof WalletTabData walletTabData) {
if(walletTabData.getWallet() == event.getWallet()) {
Label tabLabel = (Label)tab.getGraphic();
WalletIcon walletIcon = (WalletIcon)tabLabel.getGraphic();
walletIcon.refresh();
}
}
}
}
}

3
src/main/java/com/sparrowwallet/sparrow/SparrowDesktop.java

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow;
import com.sparrowwallet.drongo.Network;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.control.WalletIcon;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands;
import com.sparrowwallet.sparrow.io.Config;
@ -18,6 +19,7 @@ import org.controlsfx.tools.Platform;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;
@ -43,6 +45,7 @@ public class SparrowDesktop extends Application {
GlyphFontRegistry.register(new FontAwesome5());
GlyphFontRegistry.register(new FontAwesome5Brands());
Font.loadFont(AppServices.class.getResourceAsStream("/font/RobotoMono-Regular.ttf"), 13);
URL.setURLStreamHandlerFactory(protocol -> WalletIcon.PROTOCOL.equals(protocol) ? new WalletIcon.WalletIconStreamHandler() : null);
AppServices.initialize(this);

3
src/main/java/com/sparrowwallet/sparrow/control/PayNymAvatar.java

@ -2,6 +2,8 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.bip47.PaymentCode;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.PayNymImageLoadedEvent;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.paynym.PayNymService;
import javafx.beans.property.ObjectProperty;
@ -47,6 +49,7 @@ public class PayNymAvatar extends StackPane {
});
payNymAvatarService.setOnSucceeded(successEvent -> {
setImage(payNymAvatarService.getValue());
EventManager.get().post(new PayNymImageLoadedEvent(paymentCode, payNymAvatarService.getValue()));
});
payNymAvatarService.setOnFailed(failedEvent -> {
log.debug("Error loading PayNym avatar", failedEvent.getSource().getException());

167
src/main/java/com/sparrowwallet/sparrow/control/WalletIcon.java

@ -0,0 +1,167 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.io.ImageUtils;
import com.sparrowwallet.sparrow.io.Storage;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Pos;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Circle;
import org.controlsfx.glyphfont.Glyph;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
public class WalletIcon extends StackPane {
public static final String PROTOCOL = "walleticon";
private static final String QUERY = "icon";
public static final int WIDTH = 15;
public static final int HEIGHT = 15;
public static final int SAVE_WIDTH = WIDTH * 2;
public static final int SAVE_HEIGHT = HEIGHT * 2;
private final Storage storage;
private final ObjectProperty<Wallet> walletProperty = new SimpleObjectProperty<>();
public WalletIcon(Storage storage, Wallet wallet) {
super();
this.storage = storage;
setPrefSize(WIDTH, HEIGHT);
walletProperty.addListener((observable, oldValue, newValue) -> {
refresh();
});
walletProperty.set(wallet);
}
public void refresh() {
Wallet wallet = getWallet();
getChildren().clear();
if(wallet.getWalletConfig() != null && wallet.getWalletConfig().getIconData() != null) {
String walletId = storage.getWalletId(wallet);
if(AppServices.get().getWallet(walletId) != null) {
addWalletIcon(walletId);
} else {
Platform.runLater(() -> addWalletIcon(walletId));
}
} else if(wallet.getKeystores().size() == 1) {
Keystore keystore = wallet.getKeystores().get(0);
if(keystore.getSource() == KeystoreSource.HW_USB || keystore.getSource() == KeystoreSource.HW_AIRGAPPED) {
WalletModel walletModel = keystore.getWalletModel();
Image image = null;
try {
image = new Image("image/" + walletModel.getType() + "-icon.png", 15, 15, true, true);
} catch(Exception e) {
//ignore
}
if(image == null) {
try {
image = new Image("image/" + walletModel.getType() + ".png", 15, 15, true, true);
} catch(Exception e) {
//ignore
}
}
if(image != null && !image.isError()) {
ImageView imageView = new ImageView(image);
getChildren().add(imageView);
}
}
}
if(getChildren().isEmpty()) {
Glyph glyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.WALLET);
glyph.setFontSize(10.0);
getChildren().add(glyph);
}
}
private void addWalletIcon(String walletId) {
Image image = new Image(PROTOCOL + ":" + walletId + "?" + QUERY, WIDTH, HEIGHT, true, false);
getChildren().clear();
Circle circle = new Circle(getPrefWidth() / 2,getPrefHeight() / 2,getPrefWidth() / 2);
circle.setFill(new ImagePattern(image));
getChildren().add(circle);
}
public boolean addFailure() {
if(getChildren().stream().noneMatch(node -> node.getStyleClass().contains("failure"))) {
Glyph failGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_CIRCLE);
failGlyph.setFontSize(10);
failGlyph.getStyleClass().add("failure");
getChildren().add(failGlyph);
StackPane.setAlignment(failGlyph, Pos.TOP_RIGHT);
failGlyph.setTranslateX(5);
failGlyph.setTranslateY(-4);
return true;
}
return false;
}
public void removeFailure() {
getChildren().removeIf(node -> node.getStyleClass().contains("failure"));
}
public Wallet getWallet() {
return walletProperty.get();
}
public ObjectProperty<Wallet> walletProperty() {
return walletProperty;
}
public void setWallet(Wallet wallet) {
this.walletProperty.set(wallet);
}
public static class WalletIconStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL url) throws IOException {
return new URLConnection(url) {
@Override
public void connect() throws IOException {
//Nothing required
}
@Override
public InputStream getInputStream() throws IOException {
String walletId = url.getPath();
String query = url.getQuery();
Wallet wallet = AppServices.get().getWallet(walletId);
if(wallet == null) {
throw new IOException("Cannot find wallet for wallet id " + walletId);
}
if(query.startsWith(QUERY)) {
if(wallet.getWalletConfig() == null || wallet.getWalletConfig().getIconData() == null) {
throw new IOException("No icon data for " + walletId);
}
ByteArrayInputStream bais = new ByteArrayInputStream(wallet.getWalletConfig().getIconData());
if(query.endsWith("@2x")) {
return bais;
} else {
return ImageUtils.resize(bais, WalletIcon.WIDTH, WalletIcon.HEIGHT);
}
}
throw new MalformedURLException("Cannot load url " + url);
}
};
}
}
}

22
src/main/java/com/sparrowwallet/sparrow/event/PayNymImageLoadedEvent.java

@ -0,0 +1,22 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.bip47.PaymentCode;
import javafx.scene.image.Image;
public class PayNymImageLoadedEvent {
private final PaymentCode paymentCode;
private final Image image;
public PayNymImageLoadedEvent(PaymentCode paymentCode, Image image) {
this.paymentCode = paymentCode;
this.image = image;
}
public PaymentCode getPaymentCode() {
return paymentCode;
}
public Image getImage() {
return image;
}
}

9
src/main/java/com/sparrowwallet/sparrow/event/WalletConfigChangedEvent.java

@ -0,0 +1,9 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.wallet.Wallet;
public class WalletConfigChangedEvent extends WalletChangedEvent {
public WalletConfigChangedEvent(Wallet wallet) {
super(wallet);
}
}

56
src/main/java/com/sparrowwallet/sparrow/io/ImageUtils.java

@ -0,0 +1,56 @@
package com.sparrowwallet.sparrow.io;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import net.coobird.thumbnailator.Thumbnails;
import java.awt.image.BufferedImage;
import java.io.*;
public class ImageUtils {
public static byte[] resize(Image image, int width, int height) {
return resize(SwingFXUtils.fromFXImage(image, null), width, height);
}
public static byte[] resize(BufferedImage image, int width, int height) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resize(image, baos, width, height);
return baos.toByteArray();
} catch(IOException e) {
throw new RuntimeException(e);
}
}
public static void resize(BufferedImage image, OutputStream outputStream, int width, int height) throws IOException {
resize(Thumbnails.of(image), outputStream, width, height);
}
public static byte[] resize(File file, int width, int height) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resize(file, baos, width, height);
return baos.toByteArray();
}
public static void resize(File file, OutputStream outputStream, int width, int height) throws IOException {
resize(Thumbnails.of(file), outputStream, width, height);
}
public static InputStream resize(InputStream inputStream, int width, int height) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resize(inputStream, baos, width, height);
return new ByteArrayInputStream(baos.toByteArray());
} catch(IOException e) {
throw new RuntimeException(e);
}
}
public static void resize(InputStream inputStream, OutputStream outputStream, int width, int height) throws IOException {
resize(Thumbnails.of(inputStream), outputStream, width, height);
}
private static void resize(Thumbnails.Builder<?> builder, OutputStream outputStream, int width, int height) throws IOException {
builder.size(width, height).outputFormat("png").outputQuality(1).toOutputStream(outputStream);
}
}

14
src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java

@ -315,6 +315,11 @@ public class DbPersistence implements Persistence {
}
}
if(dirtyPersistables.walletConfig) {
WalletConfigDao walletConfigDao = handle.attach(WalletConfigDao.class);
walletConfigDao.addOrUpdate(wallet, wallet.getWalletConfig());
}
if(dirtyPersistables.mixConfig) {
MixConfigDao mixConfigDao = handle.attach(MixConfigDao.class);
mixConfigDao.addOrUpdate(wallet, wallet.getMixConfig());
@ -745,6 +750,13 @@ public class DbPersistence implements Persistence {
}
}
@Subscribe
public void walletConfigChanged(WalletConfigChangedEvent event) {
if(persistsFor(event.getWallet()) && event.getWallet().getWalletConfig() != null) {
updateExecutor.execute(() -> dirtyPersistablesMap.computeIfAbsent(event.getWallet(), key -> new DirtyPersistables()).walletConfig = true);
}
}
@Subscribe
public void walletMixConfigChanged(WalletMixConfigChangedEvent event) {
if(persistsFor(event.getWallet()) && event.getWallet().getMixConfig() != null) {
@ -793,6 +805,7 @@ public class DbPersistence implements Persistence {
public Integer watchLast = null;
public final List<Entry> labelEntries = new ArrayList<>();
public final List<BlockTransactionHashIndex> utxoStatuses = new ArrayList<>();
public boolean walletConfig;
public boolean mixConfig;
public final Map<Sha256Hash, UtxoMixData> changedUtxoMixes = new HashMap<>();
public final Map<Sha256Hash, UtxoMixData> removedUtxoMixes = new HashMap<>();
@ -812,6 +825,7 @@ public class DbPersistence implements Persistence {
"\nAddress labels:" + labelEntries.stream().filter(entry -> entry instanceof NodeEntry).map(entry -> ((NodeEntry)entry).getNode().toString() + " " + entry.getLabel()).collect(Collectors.toList()) +
"\nUTXO labels:" + labelEntries.stream().filter(entry -> entry instanceof HashIndexEntry).map(entry -> ((HashIndexEntry)entry).getHashIndex().toString()).collect(Collectors.toList()) +
"\nUTXO statuses:" + utxoStatuses +
"\nWallet config:" + walletConfig +
"\nMix config:" + mixConfig +
"\nUTXO mixes changed:" + changedUtxoMixes +
"\nUTXO mixes removed:" + removedUtxoMixes +

36
src/main/java/com/sparrowwallet/sparrow/io/db/WalletConfigDao.java

@ -0,0 +1,36 @@
package com.sparrowwallet.sparrow.io.db;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletConfig;
import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
import org.jdbi.v3.sqlobject.statement.GetGeneratedKeys;
import org.jdbi.v3.sqlobject.statement.SqlQuery;
import org.jdbi.v3.sqlobject.statement.SqlUpdate;
public interface WalletConfigDao {
@SqlQuery("select id, iconData, userIcon, usePayNym from walletConfig where wallet = ?")
@RegisterRowMapper(WalletConfigMapper.class)
WalletConfig getForWalletId(Long id);
@SqlUpdate("insert into walletConfig (iconData, userIcon, usePayNym, wallet) values (?, ?, ?, ?)")
@GetGeneratedKeys("id")
long insertWalletConfig(byte[] iconData, boolean userIcon, boolean usePayNym, long wallet);
@SqlUpdate("update walletConfig set iconData = ?, userIcon = ?, usePayNym = ?, wallet = ? where id = ?")
void updateWalletConfig(byte[] iconData, boolean userIcon, boolean usePayNym, long wallet, long id);
default void addWalletConfig(Wallet wallet) {
if(wallet.getWalletConfig() != null) {
addOrUpdate(wallet, wallet.getWalletConfig());
}
}
default void addOrUpdate(Wallet wallet, WalletConfig walletConfig) {
if(walletConfig.getId() == null) {
long id = insertWalletConfig(walletConfig.getIconData(), walletConfig.isUserIcon(), walletConfig.isUsePayNym(), wallet.getId());
walletConfig.setId(id);
} else {
updateWalletConfig(walletConfig.getIconData(), walletConfig.isUserIcon(), walletConfig.isUsePayNym(), wallet.getId(), walletConfig.getId());
}
}
}

21
src/main/java/com/sparrowwallet/sparrow/io/db/WalletConfigMapper.java

@ -0,0 +1,21 @@
package com.sparrowwallet.sparrow.io.db;
import com.sparrowwallet.drongo.wallet.WalletConfig;
import org.jdbi.v3.core.mapper.RowMapper;
import org.jdbi.v3.core.statement.StatementContext;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WalletConfigMapper implements RowMapper<WalletConfig> {
@Override
public WalletConfig map(ResultSet rs, StatementContext ctx) throws SQLException {
byte[] iconData = rs.getBytes("iconData");
boolean userIcon = rs.getBoolean("userIcon");
boolean usePayNym = rs.getBoolean("usePayNym");
WalletConfig walletConfig = new WalletConfig(iconData, userIcon, usePayNym);
walletConfig.setId(rs.getLong("id"));
return walletConfig;
}
}

10
src/main/java/com/sparrowwallet/sparrow/io/db/WalletDao.java

@ -1,10 +1,7 @@
package com.sparrowwallet.sparrow.io.db;
import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.UtxoMixData;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.drongo.wallet.*;
import org.jdbi.v3.sqlobject.CreateSqlObject;
import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
import org.jdbi.v3.sqlobject.customizer.Bind;
@ -33,6 +30,9 @@ public interface WalletDao {
@CreateSqlObject
DetachedLabelDao createDetachedLabelDao();
@CreateSqlObject
WalletConfigDao createWalletConfigDao();
@CreateSqlObject
MixConfigDao createMixConfigDao();
@ -118,6 +118,7 @@ public interface WalletDao {
Map<String, String> detachedLabels = createDetachedLabelDao().getAll();
wallet.getDetachedLabels().putAll(detachedLabels);
wallet.setWalletConfig(createWalletConfigDao().getForWalletId(wallet.getId()));
wallet.setMixConfig(createMixConfigDao().getForWalletId(wallet.getId()));
Map<Sha256Hash, UtxoMixData> utxoMixes = createUtxoMixDataDao().getForWalletId(wallet.getId());
@ -136,6 +137,7 @@ public interface WalletDao {
createWalletNodeDao().addWalletNodes(wallet);
createBlockTransactionDao().addBlockTransactions(wallet);
createDetachedLabelDao().clearAndAddAll(wallet);
createWalletConfigDao().addWalletConfig(wallet);
createMixConfigDao().addMixConfig(wallet);
createUtxoMixDataDao().addUtxoMixData(wallet);
} finally {

29
src/main/java/com/sparrowwallet/sparrow/paynym/PayNymController.java

@ -165,7 +165,7 @@ public class PayNymController {
followersList.setSelectionModel(new NoSelectionModel<>());
followersList.setFocusTraversable(false);
if(Config.get().isUsePayNym() && AppServices.isConnected() && masterWallet.hasPaymentCode()) {
if(isUsePayNym(masterWallet) && AppServices.isConnected() && masterWallet.hasPaymentCode()) {
refresh();
} else {
payNymName.setVisible(false);
@ -260,9 +260,9 @@ public class PayNymController {
}
public void retrievePayNym(ActionEvent event) {
Config.get().setUsePayNym(true);
PayNymService payNymService = AppServices.getPayNymService();
Wallet masterWallet = getMasterWallet();
setUsePayNym(masterWallet, true);
payNymService.createPayNym(masterWallet).subscribe(createMap -> {
payNymName.setText((String)createMap.get("nymName"));
payNymAvatar.setPaymentCode(masterWallet.getPaymentCode());
@ -630,6 +630,31 @@ public class PayNymController {
return wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
}
protected boolean isUsePayNym(Wallet wallet) {
//TODO: Remove config setting
boolean usePayNym = Config.get().isUsePayNym();
if(usePayNym && wallet != null) {
setUsePayNym(wallet, true);
}
return usePayNym;
}
protected void setUsePayNym(Wallet wallet, boolean usePayNym) {
//TODO: Remove config setting
if(Config.get().isUsePayNym() != usePayNym) {
Config.get().setUsePayNym(usePayNym);
}
if(wallet != null) {
WalletConfig walletConfig = wallet.getMasterWalletConfig();
if(walletConfig.isUsePayNym() != usePayNym) {
walletConfig.setUsePayNym(usePayNym);
EventManager.get().post(new WalletConfigChangedEvent(wallet.isMasterWallet() ? wallet : wallet.getMasterWallet()));
}
}
}
public boolean isSelectLinkedOnly() {
return selectLinkedOnly;
}

8
src/main/java/com/sparrowwallet/sparrow/soroban/CounterpartyController.java

@ -174,7 +174,7 @@ public class CounterpartyController extends SorobanController {
payNymAvatar.visibleProperty().bind(payNym.visibleProperty());
payNymButton.managedProperty().bind(payNymButton.visibleProperty());
payNymButton.visibleProperty().bind(payNym.visibleProperty().not());
if(Config.get().isUsePayNym()) {
if(isUsePayNym(wallet)) {
retrievePayNym(null);
} else {
payNym.setVisible(false);
@ -270,7 +270,7 @@ public class CounterpartyController extends SorobanController {
private void updateMixPartner(PaymentCode paymentCodeInitiator, CahootsType cahootsType) {
String code = paymentCodeInitiator.toString();
mixingPartner.setText(code.substring(0, 12) + "..." + code.substring(code.length() - 5));
if(Config.get().isUsePayNym()) {
if(isUsePayNym(wallet)) {
mixPartnerAvatar.setPaymentCode(paymentCodeInitiator);
AppServices.getPayNymService().getPayNym(paymentCodeInitiator.toString()).subscribe(payNym -> {
mixingPartner.setText(payNym.nymName());
@ -351,7 +351,7 @@ public class CounterpartyController extends SorobanController {
}
private void followPaymentCode(PaymentCode paymentCodeInitiator) {
if(Config.get().isUsePayNym()) {
if(isUsePayNym(wallet)) {
PayNymService payNymService = AppServices.getPayNymService();
payNymService.getAuthToken(wallet, new HashMap<>()).subscribe(authToken -> {
String signature = payNymService.getSignature(wallet, authToken);
@ -393,7 +393,7 @@ public class CounterpartyController extends SorobanController {
}
public void retrievePayNym(ActionEvent event) {
Config.get().setUsePayNym(true);
setUsePayNym(wallet, true);
PayNymService payNymService = AppServices.getPayNymService();
payNymService.createPayNym(wallet).subscribe(createMap -> {

6
src/main/java/com/sparrowwallet/sparrow/soroban/InitiatorController.java

@ -158,7 +158,7 @@ public class InitiatorController extends SorobanController {
if(newValue != null) {
if(newValue.startsWith("P") && newValue.contains("...") && newValue.length() == 20 && counterpartyPaymentCode.get() != null) {
//Assumed valid payment code
} else if(Config.get().isUsePayNym() && PAYNYM_REGEX.matcher(newValue).matches()) {
} else if(isUsePayNym(wallet) && PAYNYM_REGEX.matcher(newValue).matches()) {
if(!newValue.equals(counterpartyPayNymName.get())) {
searchPayNyms(newValue);
}
@ -233,7 +233,7 @@ public class InitiatorController extends SorobanController {
payNymFollowers.prefWidthProperty().bind(counterparty.widthProperty());
payNymFollowers.valueProperty().addListener((observable, oldValue, payNym) -> {
if(payNym == FIND_FOLLOWERS) {
Config.get().setUsePayNym(true);
setUsePayNym(wallet, true);
setPayNymFollowers();
} else if(payNym != null) {
counterpartyPayNymName.set(payNym.nymName());
@ -349,7 +349,7 @@ public class InitiatorController extends SorobanController {
payNymFollowers.setItems(FXCollections.observableList(followerPayNyms));
}, error -> {
if(error.getMessage().endsWith("404")) {
Config.get().setUsePayNym(false);
setUsePayNym(masterWallet, false);
AppServices.showErrorDialog("Could not retrieve PayNym", "This wallet does not have an associated PayNym or any followers yet. You can retrieve the PayNym using the Find PayNym button.");
} else {
log.warn("Could not retrieve followers: ", error);

28
src/main/java/com/sparrowwallet/sparrow/soroban/SorobanController.java

@ -10,7 +10,10 @@ import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.psbt.PSBTParseException;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.TransactionDiagram;
import com.sparrowwallet.sparrow.event.WalletConfigChangedEvent;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.net.ElectrumServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -126,4 +129,29 @@ public class SorobanController {
Taskbar.getTaskbar().requestUserAttention(true, false);
}
}
protected boolean isUsePayNym(Wallet wallet) {
//TODO: Remove config setting
boolean usePayNym = Config.get().isUsePayNym();
if(usePayNym && wallet != null) {
setUsePayNym(wallet, true);
}
return usePayNym;
}
protected void setUsePayNym(Wallet wallet, boolean usePayNym) {
//TODO: Remove config setting
if(Config.get().isUsePayNym() != usePayNym) {
Config.get().setUsePayNym(usePayNym);
}
if(wallet != null) {
WalletConfig walletConfig = wallet.getMasterWalletConfig();
if(walletConfig.isUsePayNym() != usePayNym) {
walletConfig.setUsePayNym(usePayNym);
EventManager.get().post(new WalletConfigChangedEvent(wallet.isMasterWallet() ? wallet : wallet.getMasterWallet()));
}
}
}
}

18
src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java

@ -22,6 +22,7 @@ import com.sparrowwallet.sparrow.control.*;
import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.io.Storage;
import com.sparrowwallet.sparrow.net.ExchangeSource;
import com.sparrowwallet.sparrow.paynym.PayNym;
import com.sparrowwallet.sparrow.paynym.PayNymAddress;
@ -39,6 +40,7 @@ import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.*;
import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.validation.ValidationResult;
@ -176,7 +178,7 @@ public class PaymentController extends WalletFormController implements Initializ
setGraphic(null);
} else {
setText(wallet.getFullDisplayName() + (wallet == sendController.getWalletForm().getWallet() ? " (Consolidation)" : ""));
setGraphic(wallet == payNymWallet ? getPayNymGlyph() : null);
setGraphic(getOpenWalletIcon(wallet));
}
}
});
@ -325,6 +327,20 @@ public class PaymentController extends WalletFormController implements Initializ
openWallets.setItems(FXCollections.observableList(openWalletList));
}
private Node getOpenWalletIcon(Wallet wallet) {
if(wallet == payNymWallet) {
return getPayNymGlyph();
}
Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
Storage storage = AppServices.get().getOpenWallets().get(masterWallet);
if(storage != null) {
return new WalletIcon(storage, masterWallet);
}
return null;
}
private void addValidation(ValidationSupport validationSupport) {
this.validationSupport = validationSupport;

23
src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java

@ -7,7 +7,9 @@ import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.WalletTabData;
import com.sparrowwallet.sparrow.control.WalletIcon;
import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.io.ImageUtils;
import com.sparrowwallet.sparrow.io.StorageException;
import com.sparrowwallet.sparrow.net.AllHistoryChangedException;
import com.sparrowwallet.sparrow.net.ElectrumServer;
@ -561,6 +563,13 @@ public class WalletForm {
}
}
@Subscribe
public void walletConfigChanged(WalletConfigChangedEvent event) {
if(event.getWallet() == wallet) {
Platform.runLater(() -> EventManager.get().post(new WalletDataChangedEvent(wallet)));
}
}
@Subscribe
public void walletMixConfigChanged(WalletMixConfigChangedEvent event) {
if(event.getWallet() == wallet) {
@ -660,4 +669,18 @@ public class WalletForm {
}
}
}
@Subscribe
public void payNymImageLoaded(PayNymImageLoadedEvent event) {
if(wallet.isMasterWallet() && wallet.hasPaymentCode() && event.getPaymentCode().equals(wallet.getPaymentCode())) {
WalletConfig walletConfig = wallet.getMasterWalletConfig();
if(!walletConfig.isUserIcon()) {
byte[] iconData = ImageUtils.resize(event.getImage(), WalletIcon.SAVE_WIDTH, WalletIcon.SAVE_HEIGHT);
if(walletConfig.getIconData() == null || !Arrays.equals(walletConfig.getIconData(), iconData)) {
walletConfig.setIconData(iconData, false);
EventManager.get().post(new WalletConfigChangedEvent(wallet));
}
}
}
}
}

1
src/main/java/module-info.java

@ -46,4 +46,5 @@ open module com.sparrowwallet.sparrow {
requires co.nstant.in.cbor;
requires com.github.librepdf.openpdf;
requires com.googlecode.lanterna;
requires net.coobird.thumbnailator;
}

1
src/main/resources/com/sparrowwallet/sparrow/sql/V8__WalletConfig.sql

@ -0,0 +1 @@
create table walletConfig (id identity not null, iconData varbinary(4096), userIcon boolean, usePayNym boolean, wallet bigint not null);

BIN
src/main/resources/image/bitbox02-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

BIN
src/main/resources/image/bitbox02-icon@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/main/resources/image/coldcard-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

BIN
src/main/resources/image/coldcard-icon@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

BIN
src/main/resources/image/ledger-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/main/resources/image/ledger-icon@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
src/main/resources/image/passport-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

BIN
src/main/resources/image/passport-icon@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/main/resources/image/seedsigner-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/main/resources/image/seedsigner-icon@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/main/resources/image/trezor-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/main/resources/image/trezor-icon@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Loading…
Cancel
Save