Skip to content

Commit

Permalink
Improve wallet unlocking UI
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Apr 25, 2024
1 parent 1b1e9f1 commit e938cf0
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public AccountKey getPublicKey() {
}

@Override
public AKeyPair getKeyPair() {
if (keyPair == null) throw new IllegalStateException("Wallet not unlocked!");
public synchronized AKeyPair getKeyPair() {
if (isLocked()) throw new IllegalStateException("Wallet not unlocked!");
return keyPair;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;

import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import convex.api.Convex;
import convex.core.crypto.AKeyPair;
import convex.core.crypto.wallet.AWalletEntry;
import convex.core.crypto.wallet.IWallet;
import convex.core.data.ACell;
Expand Down Expand Up @@ -65,8 +68,25 @@ public void focusLost(FocusEvent e) {

keyCombo=KeyPairCombo.forConvex(convex);
keyCombo.addItemListener(e->{
if (e.getStateChange()==ItemEvent.DESELECTED) return;
AWalletEntry we=(AWalletEntry)e.getItem();
convex.setKeyPair(we.getKeyPair());
AKeyPair kp;
if (we.isLocked()) {
String s=JOptionPane.showInputDialog(AccountChooserPanel.this,"Enter password to unlock wallet:\n"+we.getPublicKey());
if (s==null) {
return;
}
char[] pass=s.toCharArray();
boolean unlock=we.tryUnlock(s.toCharArray());
if (!unlock) {
return;
}
kp=we.getKeyPair();
we.lock(pass);
} else {
kp=we.getKeyPair();
}
convex.setKeyPair(kp);
});
mp.add(keyCombo);

Expand Down
75 changes: 53 additions & 22 deletions convex-gui/src/main/java/convex/gui/keys/UnlockWalletDialog.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package convex.gui.keys;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
Expand All @@ -11,20 +12,24 @@
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.KeyStroke;

import convex.core.crypto.wallet.AWalletEntry;
import convex.gui.components.Identicon;
import convex.gui.utils.Toolkit;
import net.miginfocom.swing.MigLayout;

@SuppressWarnings("serial")
public class UnlockWalletDialog extends JDialog {
private JPasswordField passwordField;

private char[] passPhrase = null;

public static UnlockWalletDialog show(WalletComponent parent) {
UnlockWalletDialog dialog = new UnlockWalletDialog(parent);
public static UnlockWalletDialog show(Component parent, AWalletEntry walletEntry) {
UnlockWalletDialog dialog = new UnlockWalletDialog(walletEntry);
dialog.setLocationRelativeTo(parent);
dialog.setVisible(true);
return dialog;
Expand All @@ -34,41 +39,48 @@ public char[] getPassPhrase() {
return passPhrase;
}

public UnlockWalletDialog(WalletComponent walletComponent) {
public UnlockWalletDialog(AWalletEntry walletEntry) {
this.setIconImage(Toolkit.WARNING.getImage());
setAlwaysOnTop(true);

setModalityType(ModalityType.DOCUMENT_MODAL);
setTitle("Unlock Wallet");
setModal(true);

JPanel panel_2 = new JPanel();
getContentPane().add(panel_2, BorderLayout.NORTH);
panel_2.setLayout(new BorderLayout(0, 0));
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new MigLayout("","","[fill]"));
mainPanel.setBorder(Toolkit.createDialogBorder());
getContentPane().add(mainPanel, BorderLayout.NORTH);

JPanel panel_1 = new JPanel();
panel_2.add(panel_1, BorderLayout.SOUTH);
Identicon a = new Identicon(walletEntry.getIdenticonData());
a.setText("0x"+walletEntry.getPublicKey().toChecksumHex());
mainPanel.add(a,"span");

// Unlock prompt and password
JPanel passPanel = new JPanel();
JLabel lblPassphrase = new JLabel("Unlock Password: ");
passPanel.add(lblPassphrase);

passwordField = new JPasswordField();
passwordField.setFont(new Font("Monospaced", Font.BOLD, 13));
passwordField.setColumns(20);
passPanel.add(passwordField);
mainPanel.add(passPanel);


// Dialog buttons
JPanel buttonPanel = new JPanel();
JButton btnUnlock = new JButton("Unlock");
panel_1.add(btnUnlock);
buttonPanel.add(btnUnlock);
btnUnlock.addActionListener(e -> {
this.passPhrase = passwordField.getPassword();
close();
});
JButton btnCancel = new JButton("Cancel");
panel_1.add(btnCancel);

JPanel panel = new JPanel();
panel_2.add(panel);

JLabel lblPassphrase = new JLabel("Password: ");
panel.add(lblPassphrase);

passwordField = new JPasswordField();
passwordField.setFont(new Font("Monospaced", Font.BOLD, 13));
passwordField.setColumns(20);
panel.add(passwordField);
buttonPanel.add(btnCancel);
btnCancel.addActionListener(e -> close());
mainPanel.add(buttonPanel, "dock south");


Action closeAction = new AbstractAction() {
@Override
Expand All @@ -82,12 +94,31 @@ public void actionPerformed(ActionEvent e) {
getRootPane().getActionMap().put("close", closeAction);

pack(); // set dialog to correct size given contents

passwordField.requestFocus();
}

public void close() {
passwordField = null;
setVisible(false);
}

/**
* Shows a dialog to ask the user to unlock a wallet
* @param parent Parent component
* @param walletEntry Wallet Entry to consider
* @return True if unlocked, false otherwise
*/
public static boolean offerUnlock(Component parent, AWalletEntry walletEntry) {
UnlockWalletDialog dialog = UnlockWalletDialog.show(parent,walletEntry);
char[] passPhrase = dialog.getPassPhrase();
if (passPhrase!=null) {
try {
walletEntry.unlock(passPhrase);
} catch (Exception e1) {
JOptionPane.showMessageDialog(parent, "Unable to unlock keypair: " + e1.getMessage(),"Unlock Failed",JOptionPane.WARNING_MESSAGE);
}
}
return !walletEntry.isLocked();
}

}
37 changes: 19 additions & 18 deletions convex-gui/src/main/java/convex/gui/keys/WalletComponent.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ public class WalletComponent extends BaseListComponent {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(WalletComponent.class.getName());

Icon icon = Toolkit.LOCKED_ICON;

JButton lockButton;
JButton replButton;

AWalletEntry walletEntry;

JPanel buttons = new JPanel();

private CodeLabel infoLabel;

public WalletComponent(AWalletEntry initialWalletEntry) {
this.walletEntry = initialWalletEntry;

Expand All @@ -54,7 +54,8 @@ public WalletComponent(AWalletEntry initialWalletEntry) {
//CodeLabel addressLabel = new CodeLabel(address.toString());
//addressLabel.setFont(Toolkit.MONO_FONT);
// cPanel.add(addressLabel,"span");
CodeLabel infoLabel = new CodeLabel(getInfoString());

infoLabel = new CodeLabel(getInfoString());
cPanel.add(infoLabel,"span,growx");
add(cPanel,"grow,shrink"); // add to MigLayout

Expand All @@ -63,18 +64,9 @@ public WalletComponent(AWalletEntry initialWalletEntry) {
// lock button
lockButton = new JButton("");
buttons.add(lockButton);
lockButton.setIcon(walletEntry.isLocked() ? Toolkit.LOCKED_ICON : Toolkit.UNLOCKED_ICON);
resetTooltipTExt(lockButton);
lockButton.addActionListener(e -> {
if (walletEntry.isLocked()) {
UnlockWalletDialog dialog = UnlockWalletDialog.show(WalletComponent.this);
char[] passPhrase = dialog.getPassPhrase();
try {
walletEntry.unlock(passPhrase);
icon = Toolkit.UNLOCKED_ICON;
} catch (Exception e1) {
JOptionPane.showMessageDialog(WalletComponent.this, "Unable to unlock keypair: " + e1.getMessage(),"Unlock Failed",JOptionPane.WARNING_MESSAGE);
}
UnlockWalletDialog.offerUnlock(this,walletEntry);
} else {
try {
String s=JOptionPane.showInputDialog(WalletComponent.this,"Enter lock password");
Expand All @@ -83,11 +75,9 @@ public WalletComponent(AWalletEntry initialWalletEntry) {
}
} catch (IllegalStateException e1) {
e1.printStackTrace();
}
icon = Toolkit.LOCKED_ICON;
}
}
resetTooltipTExt(lockButton);
lockButton.setIcon(icon);
doUpdate();
});

// Menu Button
Expand Down Expand Up @@ -124,10 +114,21 @@ public WalletComponent(AWalletEntry initialWalletEntry) {

// panel of buttons on right
add(buttons,"east"); // add to MigLayout

doUpdate();
}


private void doUpdate() {
// TODO Auto-generated method stub
resetTooltipText(lockButton);
infoLabel.setText(getInfoString());
Icon icon=walletEntry.isLocked()? Toolkit.LOCKED_ICON:Toolkit.UNLOCKED_ICON;
this.lockButton.setIcon(icon);
}


private void resetTooltipTExt(JComponent b) {
private void resetTooltipText(JComponent b) {
if (walletEntry.isLocked()) {
b.setToolTipText("Unlock");
} else {
Expand Down
6 changes: 6 additions & 0 deletions convex-gui/src/main/java/convex/gui/utils/Toolkit.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Iterator;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
Expand All @@ -31,6 +32,7 @@
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.Border;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.FontUIResource;
Expand Down Expand Up @@ -291,5 +293,9 @@ public static AWalletEntry getKeyRingEntry(AccountKey publicKey) {
return null;
}

public static Border createDialogBorder() {
return BorderFactory.createEmptyBorder(20, 20, 20, 20);
}


}

0 comments on commit e938cf0

Please sign in to comment.