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