From 03048d39b659d747980f22f3359b792ae3f15a88 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Tue, 22 Mar 2022 19:37:31 +0100 Subject: [PATCH] handle gap limit warning when creating Request. (using string error code for now, ideally should be properly defined as an enum) Also fix animation bug and work around broken ListView header implementation --- electrum/gui/qml/components/Receive.qml | 164 ++++++++++++++---------- electrum/gui/qml/qewallet.py | 33 ++--- 2 files changed, 113 insertions(+), 84 deletions(-) diff --git a/electrum/gui/qml/components/Receive.qml b/electrum/gui/qml/components/Receive.qml index 168b37702..5e80f3b7a 100644 --- a/electrum/gui/qml/components/Receive.qml +++ b/electrum/gui/qml/components/Receive.qml @@ -80,14 +80,12 @@ Pane { Layout.columnSpan: 2 text: qsTr('Create Request') onClicked: { - var a = parseFloat(amount.text) - Daemon.currentWallet.create_invoice(a, message.text, expires.currentValue) + createRequest() } } } Frame { - clip: true verticalPadding: 0 horizontalPadding: 0 @@ -103,18 +101,15 @@ Pane { color: Qt.darker(Material.background, 1.25) } - ListView { + ColumnLayout { + spacing: 0 anchors.fill: parent - model: Daemon.currentWallet.requestModel - headerPositioning: ListView.OverlayHeader - - header: Item { - z: 1 - height: hitem.height - width: ListView.view.width + Item { + Layout.preferredHeight: hitem.height + Layout.preferredWidth: parent.width Rectangle { - anchors.fill: hitem + anchors.fill: parent color: Qt.lighter(Material.background, 1.25) } RowLayout { @@ -127,76 +122,107 @@ Pane { } } - delegate: Item { - z: -1 - height: item.height - width: ListView.view.width - GridLayout { - id: item - columns: 5 - Image { - Layout.rowSpan: 2 - Layout.preferredWidth: 32 - Layout.preferredHeight: 32 - source: model.type == 0 ? "../../icons/bitcoin.png" : "../../icons/lightning.png" - } - Label { - Layout.fillWidth: true - Layout.columnSpan: 2 - text: model.message - font.pixelSize: constants.fontSizeLarge - } - - Label { - text: qsTr('Amount: ') - font.pixelSize: constants.fontSizeSmall - } - Label { - text: model.amount - font.pixelSize: constants.fontSizeSmall - } - - Label { - text: qsTr('Timestamp: ') - font.pixelSize: constants.fontSizeSmall - } - Label { - text: model.timestamp - font.pixelSize: constants.fontSizeSmall - } - - Label { - text: qsTr('Status: ') - font.pixelSize: constants.fontSizeSmall - } - Label { - text: model.status - font.pixelSize: constants.fontSizeSmall + ListView { + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + + model: Daemon.currentWallet.requestModel + + delegate: ItemDelegate { + id: root + height: item.height + width: ListView.view.width + + onClicked: console.log('Request ' + index + ' clicked') + + GridLayout { + id: item + + anchors { + left: parent.left + right: parent.right + leftMargin: constants.paddingSmall + rightMargin: constants.paddingSmall + } + + columns: 5 + + Image { + Layout.rowSpan: 2 + Layout.preferredWidth: 32 + Layout.preferredHeight: 32 + source: model.type == 0 ? "../../icons/bitcoin.png" : "../../icons/lightning.png" + } + Label { + Layout.fillWidth: true + Layout.columnSpan: 2 + text: model.message + font.pixelSize: constants.fontSizeLarge + } + + Label { + text: qsTr('Amount: ') + font.pixelSize: constants.fontSizeSmall + } + Label { + text: model.amount + font.pixelSize: constants.fontSizeSmall + } + + Label { + text: qsTr('Timestamp: ') + font.pixelSize: constants.fontSizeSmall + } + Label { + text: model.timestamp + font.pixelSize: constants.fontSizeSmall + } + + Label { + text: qsTr('Status: ') + font.pixelSize: constants.fontSizeSmall + } + Label { + text: model.status + font.pixelSize: constants.fontSizeSmall + } } } + add: Transition { + NumberAnimation { properties: 'y'; from: -50; duration: 300 } + NumberAnimation { properties: 'opacity'; from: 0; to: 1.0; duration: 700 } + } + addDisplaced: Transition { + NumberAnimation { properties: 'y'; duration: 100 } + NumberAnimation { properties: 'opacity'; to: 1.0; duration: 700 * (1-from) } + } } - - add: Transition { - NumberAnimation { properties: 'y'; from: -50; duration: 300 } - NumberAnimation { properties: 'opacity'; from: 0; to: 1.0; duration: 700 } - } - addDisplaced: Transition { - NumberAnimation { properties: 'y'; duration: 100 } - } - } } + function createRequest(ignoreGaplimit = false) { + var a = parseFloat(amount.text) + Daemon.currentWallet.create_invoice(a, message.text, expires.currentValue, false, ignoreGaplimit) + } + Connections { target: Daemon.currentWallet function onRequestCreateSuccess() { message.text = '' amount.text = '' } - function onRequestCreateError(error) { - console.log(error) - var dialog = app.messageDialog.createObject(app, {'text': error}) + function onRequestCreateError(code, error) { + if (code == 'gaplimit') { + var dialog = app.messageDialog.createObject(app, {'text': error, 'yesno': true}) + dialog.yesClicked.connect(function() { + createRequest(true) + }) + } else { + console.log(error) + var dialog = app.messageDialog.createObject(app, {'text': error}) + } dialog.open() } } diff --git a/electrum/gui/qml/qewallet.py b/electrum/gui/qml/qewallet.py index 7b7c7694f..eed5cef49 100644 --- a/electrum/gui/qml/qewallet.py +++ b/electrum/gui/qml/qewallet.py @@ -34,7 +34,7 @@ class QEWallet(QObject): dataChanged = pyqtSignal() # dummy to silence warnings requestCreateSuccess = pyqtSignal() - requestCreateError = pyqtSignal([str], arguments=['error']) + requestCreateError = pyqtSignal([str,str], arguments=['code','error']) requestStatus = pyqtSignal() def on_request_status(self, event, *args): @@ -122,12 +122,12 @@ class QEWallet(QObject): tx = self.wallet.make_unsigned_transaction(coins=coins,outputs=outputs) return True - def create_bitcoin_request(self, amount: int, message: str, expiration: int) -> Optional[str]: + def create_bitcoin_request(self, amount: int, message: str, expiration: int, ignore_gap: bool) -> Optional[str]: addr = self.wallet.get_unused_address() if addr is None: - # TODO implement - return - #if not self.wallet.is_deterministic(): # imported wallet + if not self.wallet.is_deterministic(): # imported wallet + # TODO implement + return #msg = [ #_('No more addresses in your wallet.'), ' ', #_('You are using a non-deterministic wallet, which cannot create new addresses.'), ' ', @@ -137,16 +137,18 @@ class QEWallet(QObject): #if not self.question(''.join(msg)): #return #addr = self.wallet.get_receiving_address() - #else: # deterministic wallet - #if not self.question(_("Warning: The next address will not be recovered automatically if you restore your wallet from seed; you may need to add it manually.\n\nThis occurs because you have too many unused addresses in your wallet. To avoid this situation, use the existing addresses first.\n\nCreate anyway?")): - #return - #addr = self.wallet.create_new_address(False) + else: # deterministic wallet + if not ignore_gap: + self.requestCreateError.emit('gaplimit',_("Warning: The next address will not be recovered automatically if you restore your wallet from seed; you may need to add it manually.\n\nThis occurs because you have too many unused addresses in your wallet. To avoid this situation, use the existing addresses first.\n\nCreate anyway?")) + return + addr = self.wallet.create_new_address(False) + req = self.wallet.make_payment_request(addr, amount, message, expiration) try: self.wallet.add_payment_request(req) except Exception as e: self.logger.exception('Error adding payment request') - self.requestCreateError.emit(_('Error adding payment request') + ':\n' + repr(e)) + self.requestCreateError.emit('fatal',_('Error adding payment request') + ':\n' + repr(e)) else: # TODO: check this flow. Only if alias is defined in config. OpenAlias? pass @@ -155,24 +157,25 @@ class QEWallet(QObject): return addr @pyqtSlot(int, 'QString', int) - def create_invoice(self, amount: int, message: str, expiration: int, is_lightning: bool = False): + @pyqtSlot(int, 'QString', int, bool) + @pyqtSlot(int, 'QString', int, bool, bool) + def create_invoice(self, amount: int, message: str, expiration: int, is_lightning: bool = False, ignore_gap: bool = False): expiry = expiration #TODO: self.config.get('request_expiry', PR_DEFAULT_EXPIRATION_WHEN_CREATING) try: if is_lightning: if not self.wallet.lnworker.channels: - #self.show_error(_("You need to open a Lightning channel first.")) - self.requestCreateError.emit(_("You need to open a Lightning channel first.")) + self.requestCreateError.emit('fatal',_("You need to open a Lightning channel first.")) return # TODO maybe show a warning if amount exceeds lnworker.num_sats_can_receive (as in kivy) key = self.wallet.lnworker.add_request(amount, message, expiry) else: - key = self.create_bitcoin_request(amount, message, expiry) + key = self.create_bitcoin_request(amount, message, expiry, ignore_gap) if not key: return #self.address_list.update() self._addressModel.init_model() except InvoiceError as e: - self.requestCreateError.emit(_('Error creating payment request') + ':\n' + str(e)) + self.requestCreateError.emit('fatal',_('Error creating payment request') + ':\n' + str(e)) return assert key is not None