Browse Source

Merge JoinMarket-Org/joinmarket-clientserver#1016: Improve the UX of Open Wallet dialog

c706c6e1e9 Improve the UX of open wallet dialog. (Wukong)

Pull request description:

  This PR addresses the first two items in the "Next step improvements" of #932:

  1. Show all the error messages, like "Wrong Password", "File not exist", "Invalid wallet file" etc., within the same dialog, and allow the user to make changes to their decisions in the same dialog when these errors occur.

  ![image](https://user-images.githubusercontent.com/87334822/132820190-1cbbf5fd-4d14-4317-a287-3aec378c4a41.png)

  ![image](https://user-images.githubusercontent.com/87334822/132820240-0a98e50a-43fe-4449-89e6-093af1f3533c.png)

  2. Update the UX of menu action "Wallet -> Load" to also use this new Open Wallet dialog.
  In this update, I also renamed the current menu item "Load" to "Open", and added a shortcut "Ctrl+O" for this menu item, which I believe is more consistent with the expectation of the UI from most users.

ACKs for top commit:
  kristapsk:
    re-ACK c706c6e1e9

Tree-SHA512: 3d953234034859d82f06ac018eb484024272eba58397598a014cbad1faf099ffe4ac28d661e3649c17e77bb26ba9cb7af9433a5bdd856023f61192aba5d1f2e6
master
Kristaps Kaupe 4 years ago
parent
commit
1423fe389e
No known key found for this signature in database
GPG Key ID: 33E472FE870C7E5D
  1. 31
      jmqtui/jmqtui/open_wallet_dialog.py
  2. 63
      jmqtui/jmqtui/open_wallet_dialog.ui
  3. 68
      scripts/joinmarket-qt.py

31
jmqtui/jmqtui/open_wallet_dialog.py

@ -92,6 +92,30 @@ class Ui_OpenWalletDialog(object):
self.verticalLayout.addLayout(self.horizontalLayout_2) self.verticalLayout.addLayout(self.horizontalLayout_2)
self.horizontalLayout_4 = QHBoxLayout()
self.horizontalLayout_4.setSpacing(10)
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
self.errorMessageLabel = QLabel(OpenWalletDialog)
self.errorMessageLabel.setObjectName(u"errorMessageLabel")
palette = QPalette()
brush = QBrush(QColor(239, 41, 41, 255))
brush.setStyle(Qt.SolidPattern)
palette.setBrush(QPalette.Active, QPalette.WindowText, brush)
palette.setBrush(QPalette.Inactive, QPalette.WindowText, brush)
brush1 = QBrush(QColor(190, 190, 190, 255))
brush1.setStyle(Qt.SolidPattern)
palette.setBrush(QPalette.Disabled, QPalette.WindowText, brush1)
self.errorMessageLabel.setPalette(palette)
font = QFont()
font.setBold(True)
font.setWeight(75)
self.errorMessageLabel.setFont(font)
self.horizontalLayout_4.addWidget(self.errorMessageLabel)
self.verticalLayout.addLayout(self.horizontalLayout_4)
self.verticalSpacer = QSpacerItem(20, 150, QSizePolicy.Minimum, QSizePolicy.Minimum) self.verticalSpacer = QSpacerItem(20, 150, QSizePolicy.Minimum, QSizePolicy.Minimum)
self.verticalLayout.addItem(self.verticalSpacer) self.verticalLayout.addItem(self.verticalSpacer)
@ -114,9 +138,9 @@ class Ui_OpenWalletDialog(object):
self.verticalLayout.addLayout(self.horizontalLayout_3) self.verticalLayout.addLayout(self.horizontalLayout_3)
self.verticalLayout.setStretch(2, 1) self.verticalLayout.setStretch(3, 1)
QWidget.setTabOrder(self.passphraseEdit, self.chooseWalletButton) QWidget.setTabOrder(self.walletFileEdit, self.chooseWalletButton)
QWidget.setTabOrder(self.chooseWalletButton, self.walletFileEdit) QWidget.setTabOrder(self.chooseWalletButton, self.passphraseEdit)
self.retranslateUi(OpenWalletDialog) self.retranslateUi(OpenWalletDialog)
self.buttonBox.accepted.connect(OpenWalletDialog.accept) self.buttonBox.accepted.connect(OpenWalletDialog.accept)
@ -132,5 +156,6 @@ class Ui_OpenWalletDialog(object):
self.walletFileEdit.setPlaceholderText("") self.walletFileEdit.setPlaceholderText("")
self.chooseWalletButton.setText(QCoreApplication.translate("OpenWalletDialog", u"Choose...", None)) self.chooseWalletButton.setText(QCoreApplication.translate("OpenWalletDialog", u"Choose...", None))
self.label_2.setText(QCoreApplication.translate("OpenWalletDialog", u"Passphrase:", None)) self.label_2.setText(QCoreApplication.translate("OpenWalletDialog", u"Passphrase:", None))
self.errorMessageLabel.setText("")
# retranslateUi # retranslateUi

63
jmqtui/jmqtui/open_wallet_dialog.ui

@ -25,7 +25,7 @@
<property name="modal"> <property name="modal">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1,0"> <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,1,0">
<property name="spacing"> <property name="spacing">
<number>10</number> <number>10</number>
</property> </property>
@ -145,6 +145,63 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0">
<property name="spacing">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="errorMessageLabel">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>239</red>
<green>41</green>
<blue>41</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>239</red>
<green>41</green>
<blue>41</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>190</red>
<green>190</green>
<blue>190</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -194,9 +251,9 @@
</layout> </layout>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>passphraseEdit</tabstop>
<tabstop>chooseWalletButton</tabstop>
<tabstop>walletFileEdit</tabstop> <tabstop>walletFileEdit</tabstop>
<tabstop>chooseWalletButton</tabstop>
<tabstop>passphraseEdit</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections> <connections>

68
scripts/joinmarket-qt.py

@ -114,6 +114,7 @@ class JMOpenWalletDialog(QDialog, Ui_OpenWalletDialog):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.setupUi(self) self.setupUi(self)
self.passphraseEdit.setFocus()
self.chooseWalletButton.clicked.connect(self.chooseWalletFile) self.chooseWalletButton.clicked.connect(self.chooseWalletFile)
@ -125,6 +126,7 @@ class JMOpenWalletDialog(QDialog, Ui_OpenWalletDialog):
options=QFileDialog.DontUseNativeDialog) options=QFileDialog.DontUseNativeDialog)
if filename: if filename:
self.walletFileEdit.setText(filename) self.walletFileEdit.setText(filename)
self.passphraseEdit.setFocus()
class HelpLabel(QLabel): class HelpLabel(QLabel):
@ -1635,9 +1637,10 @@ class JMMainWindow(QMainWindow):
def initUI(self): def initUI(self):
self.statusBar().showMessage("Ready") self.statusBar().showMessage("Ready")
self.setGeometry(300, 300, 250, 150) self.setGeometry(300, 300, 250, 150)
loadAction = QAction('&Load...', self) openWalletAction = QAction('&Open...', self)
loadAction.setStatusTip('Load wallet from file') openWalletAction.setStatusTip('Open JoinMarket wallet file')
loadAction.triggered.connect(self.selectWallet) openWalletAction.setShortcut(QKeySequence.Open)
openWalletAction.triggered.connect(self.openWallet)
generateAction = QAction('&Generate...', self) generateAction = QAction('&Generate...', self)
generateAction.setStatusTip('Generate new wallet') generateAction.setStatusTip('Generate new wallet')
generateAction.triggered.connect(self.generateWallet) generateAction.triggered.connect(self.generateWallet)
@ -1657,7 +1660,7 @@ class JMMainWindow(QMainWindow):
receivePayjoinAction.setStatusTip('Receive BIP78 style payment') receivePayjoinAction.setStatusTip('Receive BIP78 style payment')
receivePayjoinAction.triggered.connect(self.receiver_bip78_init) receivePayjoinAction.triggered.connect(self.receiver_bip78_init)
quitAction = QAction(QIcon('exit.png'), '&Quit', self) quitAction = QAction(QIcon('exit.png'), '&Quit', self)
quitAction.setShortcut('Ctrl+Q') quitAction.setShortcut(QKeySequence.Quit)
quitAction.setStatusTip('Quit application') quitAction.setStatusTip('Quit application')
quitAction.triggered.connect(qApp.quit) quitAction.triggered.connect(qApp.quit)
@ -1666,7 +1669,7 @@ class JMMainWindow(QMainWindow):
menubar = self.menuBar() menubar = self.menuBar()
walletMenu = menubar.addMenu('&Wallet') walletMenu = menubar.addMenu('&Wallet')
walletMenu.addAction(loadAction) walletMenu.addAction(openWalletAction)
walletMenu.addAction(generateAction) walletMenu.addAction(generateAction)
walletMenu.addAction(recoverAction) walletMenu.addAction(recoverAction)
walletMenu.addAction(showSeedAction) walletMenu.addAction(showSeedAction)
@ -1960,6 +1963,28 @@ class JMMainWindow(QMainWindow):
title="Wallet created") title="Wallet created")
self.initWallet(seed=self.walletname) self.initWallet(seed=self.walletname)
def openWallet(self):
wallet_loaded = False
wallet_file_text = "wallet.jmdat"
error_text = ""
while not wallet_loaded:
openWalletDialog = JMOpenWalletDialog()
openWalletDialog.walletFileEdit.setText(wallet_file_text)
openWalletDialog.errorMessageLabel.setText(error_text)
if openWalletDialog.exec_() == QDialog.Accepted:
wallet_file_text = openWalletDialog.walletFileEdit.text()
wallet_path = wallet_file_text
if not os.path.isabs(wallet_path):
wallet_path = os.path.join(jm_single().datadir, 'wallets', wallet_path)
try:
wallet_loaded = mainWindow.loadWalletFromBlockchain(wallet_path, openWalletDialog.passphraseEdit.text(), rethrow=True)
except Exception as e:
error_text = str(e)
else:
break
def selectWallet(self, testnet_seed=None): def selectWallet(self, testnet_seed=None):
if jm_single().config.get("BLOCKCHAIN", "blockchain_source") != "regtest": if jm_single().config.get("BLOCKCHAIN", "blockchain_source") != "regtest":
# guaranteed to exist as load_program_config was called on startup: # guaranteed to exist as load_program_config was called on startup:
@ -2006,7 +2031,7 @@ class JMMainWindow(QMainWindow):
#ignore return value as there is no decryption failure possible #ignore return value as there is no decryption failure possible
self.loadWalletFromBlockchain(firstarg, pwd) self.loadWalletFromBlockchain(firstarg, pwd)
def loadWalletFromBlockchain(self, firstarg=None, pwd=None): def loadWalletFromBlockchain(self, firstarg=None, pwd=None, rethrow=False):
if firstarg: if firstarg:
wallet_path = get_wallet_path(str(firstarg), None) wallet_path = get_wallet_path(str(firstarg), None)
try: try:
@ -2014,11 +2039,14 @@ class JMMainWindow(QMainWindow):
None, ask_for_password=False, password=pwd.encode('utf-8') if pwd else None, None, ask_for_password=False, password=pwd.encode('utf-8') if pwd else None,
gap_limit=jm_single().config.getint("GUI", "gaplimit")) gap_limit=jm_single().config.getint("GUI", "gaplimit"))
except RetryableStorageError as e: except RetryableStorageError as e:
JMQtMessageBox(self, if rethrow:
str(e), raise e
mbtype='warn', else:
title="Error") JMQtMessageBox(self,
return False str(e),
mbtype='warn',
title="Error")
return False
# only used for GUI display on regtest: # only used for GUI display on regtest:
self.testwalletname = wallet.seed = str(firstarg) self.testwalletname = wallet.seed = str(firstarg)
if 'listunspent_args' not in jm_single().config.options('POLICY'): if 'listunspent_args' not in jm_single().config.options('POLICY'):
@ -2385,21 +2413,7 @@ tabWidget.currentChanged.connect(onTabChange)
mainWindow.show() mainWindow.show()
reactor.runReturn() reactor.runReturn()
# Upon launching the app, allow the user to choose a wallet to open # Upon launching the app, ask the user to choose a wallet to open
openWalletDialog = JMOpenWalletDialog() mainWindow.openWallet()
openWalletDialog.show()
if openWalletDialog.exec_() == QDialog.Accepted:
wallet_path = openWalletDialog.walletFileEdit.text()
if not os.path.isabs(wallet_path):
wallet_path = os.path.join(jm_single().datadir, 'wallets', wallet_path)
try:
mainWindow.loadWalletFromBlockchain(wallet_path, openWalletDialog.passphraseEdit.text())
except Exception as e:
JMQtMessageBox(None,
str(e),
mbtype='warn',
title="Error")
sys.exit(app.exec_()) sys.exit(app.exec_())

Loading…
Cancel
Save