From 30d6228cac631bdb03beac18aacbe3a5c12aa5d3 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 2 Jun 2025 11:52:44 +0200 Subject: [PATCH 1/9] qml: remove nostr relays from NetworkOverview.qml --- electrum/gui/qml/components/NetworkOverview.qml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/electrum/gui/qml/components/NetworkOverview.qml b/electrum/gui/qml/components/NetworkOverview.qml index 9d6bc4399..ecba3e68c 100644 --- a/electrum/gui/qml/components/NetworkOverview.qml +++ b/electrum/gui/qml/components/NetworkOverview.qml @@ -217,22 +217,6 @@ Pane { text: Daemon.currentWallet.lightningNumPeers } - Heading { - Layout.columnSpan: 2 - text: qsTr('Nostr') - } - - Label { - text: qsTr('Relays:') - color: Material.accentColor - } - - Label { - Layout.fillWidth: true - text: Config.nostrRelays.replace(/,/g, "\n") - wrapMode: Text.Wrap - } - Heading { Layout.columnSpan: 2 text: qsTr('Proxy') From b5170a3fa64615904bb517ed282a72922ed747a8 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 2 Jun 2025 13:27:28 +0200 Subject: [PATCH 2/9] qml: oneserver, auto-connect combobox --- .../gui/qml/components/ServerConfigDialog.qml | 6 +- .../qml/components/controls/ServerConfig.qml | 56 +++++++++---------- .../controls/ServerConnectModeComboBox.qml | 38 +++++++++++++ .../qml/components/wizard/WCServerConfig.qml | 5 +- 4 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 electrum/gui/qml/components/controls/ServerConnectModeComboBox.qml diff --git a/electrum/gui/qml/components/ServerConfigDialog.qml b/electrum/gui/qml/components/ServerConfigDialog.qml index b9679546e..ec53ef412 100644 --- a/electrum/gui/qml/components/ServerConfigDialog.qml +++ b/electrum/gui/qml/components/ServerConfigDialog.qml @@ -42,10 +42,8 @@ ElDialog { text: qsTr('Ok') icon.source: '../../icons/confirmed.png' onClicked: { - Network.oneServer = serverconfig.auto_connect - ? false - : serverconfig.one_server - Config.autoConnect = serverconfig.auto_connect + Network.oneServer = serverconfig.serverConnectMode == ServerConnectModeComboBox.Mode.Single + Config.autoConnect = serverconfig.serverConnectMode == ServerConnectModeComboBox.Mode.Autoconnect Network.server = serverconfig.address rootItem.close() } diff --git a/electrum/gui/qml/components/controls/ServerConfig.qml b/electrum/gui/qml/components/controls/ServerConfig.qml index 46a4cbba2..47bd4e680 100644 --- a/electrum/gui/qml/components/controls/ServerConfig.qml +++ b/electrum/gui/qml/components/controls/ServerConfig.qml @@ -10,9 +10,8 @@ Item { id: root property bool showAutoselectServer: true - property alias auto_connect: auto_server_cb.checked property alias address: address_tf.text - property alias one_server: one_server_cb.checked + property alias serverConnectMode: server_connect_mode_cb.currentValue implicitHeight: rootLayout.height @@ -23,12 +22,32 @@ Item { height: parent.height spacing: constants.paddingLarge - CheckBox { - id: auto_server_cb - visible: showAutoselectServer - text: Config.shortDescFor('NETWORK_AUTO_CONNECT') - checked: !showAutoselectServer - enabled: !one_server_cb.checked + + RowLayout { + Layout.fillWidth: true + + ServerConnectModeComboBox { + id: server_connect_mode_cb + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 1 + } + + HelpButton { + Layout.alignment: Qt.AlignRight + heading: qsTr('Connection mode')+':' + helptext: Config.getTranslatedMessage('MSG_CONNECTMODE_SERVER_HELP') + '

' + + Config.getTranslatedMessage('MSG_CONNECTMODE_NODES_HELP') + '' + } } Label { @@ -41,28 +60,12 @@ Item { TextField { id: address_tf - enabled: !auto_server_cb.checked + enabled: server_connect_mode_cb.currentValue != ServerConnectModeComboBox.Mode.Autoconnect width: parent.width inputMethodHints: Qt.ImhNoPredictiveText } } - RowLayout { - Layout.fillWidth: true - visible: !auto_server_cb.checked && address_tf.text - - CheckBox { - id: one_server_cb - Layout.fillWidth: true - text: Config.shortDescFor('NETWORK_ONESERVER') - } - - HelpButton { - heading: Config.shortDescFor('NETWORK_ONESERVER') - helptext: Config.longDescFor('NETWORK_ONESERVER') - } - } - ColumnLayout { Heading { text: qsTr('Servers') @@ -112,9 +115,6 @@ Item { } Component.onCompleted: { - root.auto_connect = Config.autoConnectDefined ? Config.autoConnect : false root.address = Network.server - one_server_cb.checked = Network.oneServer - // TODO: initial setup should not connect already, is Network.server defined? } } diff --git a/electrum/gui/qml/components/controls/ServerConnectModeComboBox.qml b/electrum/gui/qml/components/controls/ServerConnectModeComboBox.qml new file mode 100644 index 000000000..c35074550 --- /dev/null +++ b/electrum/gui/qml/components/controls/ServerConnectModeComboBox.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Controls + +import org.electrum 1.0 + +ElComboBox { + id: control + + enum Mode { + Autoconnect, + Manual, + Single + } + + textRole: 'text' + valueRole: 'value' + + model: [ + { text: qsTr('Auto-connect'), value: ServerConnectModeComboBox.Mode.Autoconnect }, + { text: qsTr('Manual server selection'), value: ServerConnectModeComboBox.Mode.Manual }, + { text: qsTr('Connect only to a single server'), value: ServerConnectModeComboBox.Mode.Single } + ] + + Component.onCompleted: { + if (!Config.autoConnectDefined) { // initial setup + server_connect_mode_cb.currentIndex = server_connect_mode_cb.indexOfValue( + ServerConnectModeComboBox.Mode.Manual) + } else { + server_connect_mode_cb.currentIndex = server_connect_mode_cb.indexOfValue( + Config.autoConnect + ? ServerConnectModeComboBox.Mode.Autoconnect + : Network.oneServer + ? ServerConnectModeComboBox.Mode.Single + : ServerConnectModeComboBox.Mode.Manual + ) + } + } +} diff --git a/electrum/gui/qml/components/wizard/WCServerConfig.qml b/electrum/gui/qml/components/wizard/WCServerConfig.qml index a48a1bd70..bc784d1b6 100644 --- a/electrum/gui/qml/components/wizard/WCServerConfig.qml +++ b/electrum/gui/qml/components/wizard/WCServerConfig.qml @@ -10,9 +10,9 @@ WizardComponent { title: qsTr('Server') function apply() { - wizard_data['autoconnect'] = sc.address.trim() == "" wizard_data['server'] = sc.address - wizard_data['one_server'] = sc.one_server + wizard_data['autoconnect'] = sc.serverConnectMode == ServerConnectModeComboBox.Mode.Autoconnect + wizard_data['one_server'] = sc.serverConnectMode == ServerConnectModeComboBox.Mode.Single } ColumnLayout { @@ -21,7 +21,6 @@ WizardComponent { ServerConfig { id: sc - showAutoselectServer: false Layout.fillWidth: true Layout.fillHeight: true } From 5fe6ff3a1f4413fcb9557d19cc75153561818249 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 2 Jun 2025 13:42:12 +0200 Subject: [PATCH 3/9] qml: nostr relay dialog, remove help button, move text into dialog. --- .../gui/qml/components/NostrConfigDialog.qml | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/electrum/gui/qml/components/NostrConfigDialog.qml b/electrum/gui/qml/components/NostrConfigDialog.qml index 3f754fa80..4ce2727f7 100644 --- a/electrum/gui/qml/components/NostrConfigDialog.qml +++ b/electrum/gui/qml/components/NostrConfigDialog.qml @@ -51,19 +51,15 @@ ElDialog { Layout.leftMargin: constants.paddingLarge Layout.rightMargin: constants.paddingLarge - RowLayout { + TextHighlightPane { Layout.fillWidth: true - TextHighlightPane { - Layout.fillWidth: true - Label { - text: qsTr('Enter the list of Nostr relays') - width: parent.width - wrapMode: Text.Wrap - } - } - HelpButton { - heading: Config.shortDescFor('NOSTR_RELAYS') - helptext: Config.longDescFor('NOSTR_RELAYS') + Label { + text: Config.shortDescFor('NOSTR_RELAYS') + '

' + + qsTr('Nostr relays are used to send and receive submarine swap offers.') + + ' ' + qsTr('For multisig wallets, nostr is also used to relay transactions to your co-signers.') + + ' ' + qsTr('Connections to nostr are only made when required, and ephemerally.') + width: parent.width + wrapMode: Text.Wrap } } From 5775fd790eb613f5ca8bb8ed07445236d666de05 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 2 Jun 2025 13:47:49 +0200 Subject: [PATCH 4/9] qml: remove request from list once paid --- electrum/gui/qml/qeinvoicelistmodel.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/electrum/gui/qml/qeinvoicelistmodel.py b/electrum/gui/qml/qeinvoicelistmodel.py index a7825d276..9c3442da4 100644 --- a/electrum/gui/qml/qeinvoicelistmodel.py +++ b/electrum/gui/qml/qeinvoicelistmodel.py @@ -6,7 +6,7 @@ from PyQt6.QtCore import Qt, QAbstractListModel, QModelIndex from electrum.logging import get_logger from electrum.util import Satoshis, format_time -from electrum.invoices import BaseInvoice, PR_EXPIRED, LN_EXPIRY_NEVER, Invoice, Request +from electrum.invoices import BaseInvoice, PR_EXPIRED, LN_EXPIRY_NEVER, Invoice, Request, PR_PAID from .util import QtEventListener, qt_event_listener, status_update_timer_interval from .qetypes import QEAmount @@ -247,4 +247,7 @@ class QERequestListModel(QEAbstractInvoiceListModel, QtEventListener): @pyqtSlot(str, int) def updateRequest(self, key, status): - self.updateInvoice(key, status) + if status == PR_PAID: + self.delete_invoice(key) + else: + self.updateInvoice(key, status) From 938af7b2eea38a53f78dffb68c04a00cedc4879d Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 2 Jun 2025 17:09:47 +0200 Subject: [PATCH 5/9] qml: remove green check-mark on request paid, open lightning payment details (LN) or Tx details (on-chain) instead --- electrum/gui/qml/components/ReceiveDialog.qml | 47 ++++--------------- .../gui/qml/components/WalletMainView.qml | 9 ++++ electrum/gui/qml/qerequestdetails.py | 14 +++++- 3 files changed, 30 insertions(+), 40 deletions(-) diff --git a/electrum/gui/qml/components/ReceiveDialog.qml b/electrum/gui/qml/components/ReceiveDialog.qml index 45a94918c..19982d05f 100644 --- a/electrum/gui/qml/components/ReceiveDialog.qml +++ b/electrum/gui/qml/components/ReceiveDialog.qml @@ -12,23 +12,25 @@ ElDialog { id: dialog title: qsTr('Receive Payment') + iconSource: Qt.resolvedUrl('../../icons/tab_receive.png') property string key + property bool isLightning: request.isLightning property string _bolt11: request.bolt11 property string _bip21uri: request.bip21 property string _address: request.address - property bool _render_qr: false // delay qr rendering until dialog is shown - property bool _ispaid: false - - iconSource: Qt.resolvedUrl('../../icons/tab_receive.png') + signal requestPaid padding: 0 + function getPaidTxid() { + return request.paidTxid + } + ColumnLayout { - visible: !_ispaid anchors.fill: parent spacing: 0 @@ -40,7 +42,7 @@ ElDialog { rightMargin: constants.paddingLarge contentHeight: rootLayout.height - clip:true + clip: true interactive: height < contentHeight ColumnLayout { @@ -186,43 +188,12 @@ ElDialog { } } - ColumnLayout { - visible: _ispaid - anchors.centerIn: parent - states: [ - State { - name: 'paid' - when: _ispaid - } - ] - transitions: [ - Transition { - from: '' - to: 'paid' - NumberAnimation { target: paidIcon; properties: 'opacity'; from: 0; to: 1; duration: 200 } - NumberAnimation { target: paidIcon; properties: 'scale'; from: 0; to: 1; duration: 500; easing.type: Easing.OutBack; easing.overshoot: 10 } - } - ] - Image { - id: paidIcon - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: constants.iconSizeXXLarge - Layout.preferredHeight: constants.iconSizeXXLarge - source: '../../icons/confirmed.png' - } - Label { - Layout.alignment: Qt.AlignHCenter - text: qsTr('Paid!') - font.pixelSize: constants.fontSizeXXLarge - } - } - RequestDetails { id: request wallet: Daemon.currentWallet onStatusChanged: { if (status == RequestDetails.Paid || status == RequestDetails.Unconfirmed) { - _ispaid = true + requestPaid() } } } diff --git a/electrum/gui/qml/components/WalletMainView.qml b/electrum/gui/qml/components/WalletMainView.qml index 50d13c5b7..e2d7229ca 100644 --- a/electrum/gui/qml/components/WalletMainView.qml +++ b/electrum/gui/qml/components/WalletMainView.qml @@ -639,6 +639,15 @@ Item { width: parent.width height: parent.height + onRequestPaid: { + close() + if (isLightning) { + app.stack.push(Qt.resolvedUrl('LightningPaymentDetails.qml'), {'key': key}) + } else { + let paidTxid = getPaidTxid() + app.stack.push(Qt.resolvedUrl('TxDetails.qml'), {'txid': paidTxid}) + } + } onClosed: destroy() } } diff --git a/electrum/gui/qml/qerequestdetails.py b/electrum/gui/qml/qerequestdetails.py index 319d512bd..6f5e35dc1 100644 --- a/electrum/gui/qml/qerequestdetails.py +++ b/electrum/gui/qml/qerequestdetails.py @@ -29,7 +29,7 @@ class QERequestDetails(QObject, QtEventListener): _logger = get_logger(__name__) - detailsChanged = pyqtSignal() # generic request properties changed signal + detailsChanged = pyqtSignal() # generic request properties changed signal statusChanged = pyqtSignal() def __init__(self, parent=None): @@ -95,7 +95,7 @@ class QERequestDetails(QObject, QtEventListener): @pyqtProperty(bool, notify=detailsChanged) def isLightning(self): - return self._req.is_lightning() + return self._req.is_lightning() if self._req else False @pyqtProperty(str, notify=detailsChanged) def address(self): @@ -118,6 +118,16 @@ class QERequestDetails(QObject, QtEventListener): def expiration(self): return self._req.get_expiration_date() + @pyqtProperty(str, notify=statusChanged) + def paidTxid(self): + """only used when Request status is PR_PAID""" + if not self._req: + return '' + is_paid, conf_needed, txids = self._wallet.wallet._is_onchain_invoice_paid(self._req) + if len(txids) > 0: + return txids[0] + return '' + @pyqtProperty(str, notify=detailsChanged) def bolt11(self): wallet = self._wallet.wallet From df2c1d6ae1ecf6c44d99fc0dd6ac3ca3e8ff83ca Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 2 Jun 2025 17:14:31 +0200 Subject: [PATCH 6/9] qml: no feebump hint if only option is CPFP --- electrum/gui/qml/components/TxDetails.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrum/gui/qml/components/TxDetails.qml b/electrum/gui/qml/components/TxDetails.qml index 12cb2d01a..42f462a2e 100644 --- a/electrum/gui/qml/components/TxDetails.qml +++ b/electrum/gui/qml/components/TxDetails.qml @@ -75,7 +75,7 @@ Pane { : txdetails.isRemoved ? qsTr('This transaction has been replaced or removed and is no longer valid') : txdetails.inMempool ? qsTr('This transaction is still unconfirmed.') + - (txdetails.canBump || txdetails.canCpfp || txdetails.canCancel + (txdetails.canBump || txdetails.canCancel ? txdetails.canCancel ? '\n' + qsTr('You can bump its fee to speed up its confirmation, or cancel this transaction.') : '\n' + qsTr('You can bump its fee to speed up its confirmation.') From c6fb55d416b2bcfb8e1b4eb8062ae320a49a8dee Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Mon, 2 Jun 2025 17:21:42 +0200 Subject: [PATCH 7/9] qml: lightningpaymentdetails show Paid banner instead of just Status:settled --- .../gui/qml/components/LightningPaymentDetails.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/electrum/gui/qml/components/LightningPaymentDetails.qml b/electrum/gui/qml/components/LightningPaymentDetails.qml index 31ea3bfda..2edbacd90 100644 --- a/electrum/gui/qml/components/LightningPaymentDetails.qml +++ b/electrum/gui/qml/components/LightningPaymentDetails.qml @@ -33,13 +33,13 @@ Pane { text: qsTr('Lightning payment details') } - Label { - text: qsTr('Status') - color: Material.accentColor - } - - Label { - text: lnpaymentdetails.status + InfoTextArea { + Layout.columnSpan: 2 + Layout.fillWidth: true + Layout.bottomMargin: constants.paddingLarge + visible: text + text: lnpaymentdetails.status ? qsTr('Paid') : '' + iconStyle: InfoTextArea.IconStyle.Done } Label { From d87b0f8e81c998effd66d16b1c8f4c2ff335062c Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Tue, 3 Jun 2025 10:50:28 +0200 Subject: [PATCH 8/9] qml: improve nostr dialog helptext --- electrum/gui/qml/components/NostrConfigDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrum/gui/qml/components/NostrConfigDialog.qml b/electrum/gui/qml/components/NostrConfigDialog.qml index 4ce2727f7..767b3c42f 100644 --- a/electrum/gui/qml/components/NostrConfigDialog.qml +++ b/electrum/gui/qml/components/NostrConfigDialog.qml @@ -54,7 +54,7 @@ ElDialog { TextHighlightPane { Layout.fillWidth: true Label { - text: Config.shortDescFor('NOSTR_RELAYS') + '

' + + text: qsTr('Enter the list of Nostr relays') + '

' + qsTr('Nostr relays are used to send and receive submarine swap offers.') + ' ' + qsTr('For multisig wallets, nostr is also used to relay transactions to your co-signers.') + ' ' + qsTr('Connections to nostr are only made when required, and ephemerally.') From cb78b07fe487d61f5ab2e9cbaa6899e2f3f02673 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Tue, 3 Jun 2025 10:57:43 +0200 Subject: [PATCH 9/9] qml: reset nostr relays list to default if empty --- electrum/gui/qml/qeconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrum/gui/qml/qeconfig.py b/electrum/gui/qml/qeconfig.py index efc41032a..01fc4eea6 100644 --- a/electrum/gui/qml/qeconfig.py +++ b/electrum/gui/qml/qeconfig.py @@ -318,7 +318,7 @@ class QEConfig(AuthMixin, QObject): @nostrRelays.setter def nostrRelays(self, nostr_relays): if nostr_relays != self.config.NOSTR_RELAYS: - self.config.NOSTR_RELAYS = nostr_relays + self.config.NOSTR_RELAYS = nostr_relays if nostr_relays else None self.nostrRelaysChanged.emit() swapServerNPubChanged = pyqtSignal()