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 { 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') diff --git a/electrum/gui/qml/components/NostrConfigDialog.qml b/electrum/gui/qml/components/NostrConfigDialog.qml index 3f754fa80..767b3c42f 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: 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.') + width: parent.width + wrapMode: Text.Wrap } } 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/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/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.') 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/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 } 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() 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) 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