From 509e0fcc07b7f483e2df46b8eab6552286b21624 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Tue, 12 Jul 2022 17:48:03 +0200 Subject: [PATCH] qml: initial channel open progress dialog --- .../components/ChannelOpenProgressDialog.qml | 110 ++++++++++++++++++ electrum/gui/qml/components/OpenChannel.qml | 23 +++- electrum/gui/qml/qechannelopener.py | 39 ++++--- 3 files changed, 153 insertions(+), 19 deletions(-) create mode 100644 electrum/gui/qml/components/ChannelOpenProgressDialog.qml diff --git a/electrum/gui/qml/components/ChannelOpenProgressDialog.qml b/electrum/gui/qml/components/ChannelOpenProgressDialog.qml new file mode 100644 index 000000000..a06d092e7 --- /dev/null +++ b/electrum/gui/qml/components/ChannelOpenProgressDialog.qml @@ -0,0 +1,110 @@ +import QtQuick 2.6 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 2.3 +import QtQuick.Controls.Material 2.0 + +import org.electrum 1.0 + +import "controls" + +ElDialog { + id: dialog + width: parent.width + height: parent.height + + title: qsTr('Opening Channel...') + standardButtons: Dialog.Close + footer.visible: allowClose // work around standardButtons not really mutable to/from zero buttons + allowClose: false + + modal: true + parent: Overlay.overlay + Overlay.modal: Rectangle { + color: "#aa000000" + } + + property alias state: s.state + property alias error: errorText.text + property alias peer: peerText.text + + Item { + id: s + state: '' + states: [ + State { + name: 'success' + PropertyChanges { target: dialog; allowClose: true } + PropertyChanges { target: stateText; text: qsTr('Success!') } + PropertyChanges { target: infoText; visible: true } + PropertyChanges { target: icon; source: '../../icons/confirmed.png' } + }, + State { + name: 'failed' + PropertyChanges { target: dialog; allowClose: true } + PropertyChanges { target: stateText; text: qsTr('Problem opening channel') } + PropertyChanges { target: errorText; visible: true } + PropertyChanges { target: icon; source: '../../icons/warning.png' } + } + ] + } + + ColumnLayout { + id: content + anchors.centerIn: parent + width: parent.width + + RowLayout { + Layout.alignment: Qt.AlignHCenter + Image { + id: icon + source: '' + visible: source != '' + Layout.preferredWidth: constants.iconSizeLarge + Layout.preferredHeight: constants.iconSizeLarge + } + BusyIndicator { + id: spinner + running: visible + visible: s.state == '' + Layout.preferredWidth: constants.iconSizeLarge + Layout.preferredHeight: constants.iconSizeLarge + } + + Label { + id: stateText + text: qsTr('Opening Channel...') + font.pixelSize: constants.fontSizeXXLarge + } + } + + TextHighlightPane { + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: dialog.width * 3/4 + Label { + id: peerText + font.pixelSize: constants.fontSizeMedium + width: parent.width + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + } + } + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: dialog.width * 2/3 + InfoTextArea { + id: errorText + visible: false + iconStyle: InfoTextArea.IconStyle.Error + width: parent.width + } + + InfoTextArea { + id: infoText + visible: false + text: qsTr('Channel will be open after 3 confirmations') + } + } + } + +} diff --git a/electrum/gui/qml/components/OpenChannel.qml b/electrum/gui/qml/components/OpenChannel.qml index a76259e44..2950284ad 100644 --- a/electrum/gui/qml/components/OpenChannel.qml +++ b/electrum/gui/qml/components/OpenChannel.qml @@ -157,10 +157,18 @@ Pane { } } + property var channelOpenProgressDialogInstance + Component { + id: channelOpenProgressDialog + ChannelOpenProgressDialog { } + } ChannelOpener { id: channelopener wallet: Daemon.currentWallet + onAuthRequired: { + app.handleAuthRequired(channelopener, method) + } onValidationError: { if (code == 'invalid_nodeid') { var dialog = app.messageDialog.createObject(root, { 'text': message }) @@ -180,18 +188,23 @@ Pane { }) dialog.open() } + onChannelOpening: { + console.log('Channel is opening') + channelOpenProgressDialogInstance = channelOpenProgressDialog.createObject(app, {peer: peer}) + channelOpenProgressDialogInstance.open() + } onChannelOpenError: { - var dialog = app.messageDialog.createObject(root, { 'text': message }) - dialog.open() + channelOpenProgressDialogInstance.state = 'failed' + channelOpenProgressDialogInstance.error = message } onChannelOpenSuccess: { var message = 'success!' if (!has_backup) message = message + ' (but no backup. TODO: show QR)' - var dialog = app.messageDialog.createObject(root, { 'text': message }) - dialog.open() + channelOpenProgressDialogInstance.state = 'success' + channelOpenProgressDialogInstance.error = message channelopener.wallet.channelModel.new_channel(cid) - app.stack.pop() +// app.stack.pop() } } diff --git a/electrum/gui/qml/qechannelopener.py b/electrum/gui/qml/qechannelopener.py index 5e9f1ffcd..593313304 100644 --- a/electrum/gui/qml/qechannelopener.py +++ b/electrum/gui/qml/qechannelopener.py @@ -1,3 +1,5 @@ +import threading + from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from electrum.i18n import _ @@ -9,8 +11,9 @@ from electrum.gui import messages from .qewallet import QEWallet from .qetypes import QEAmount from .qetxfinalizer import QETxFinalizer +from .auth import AuthMixin, auth_protect -class QEChannelOpener(QObject): +class QEChannelOpener(QObject, AuthMixin): def __init__(self, parent=None): super().__init__(parent) @@ -24,6 +27,7 @@ class QEChannelOpener(QObject): validationError = pyqtSignal([str,str], arguments=['code','message']) conflictingBackup = pyqtSignal([str], arguments=['message']) + channelOpening = pyqtSignal([str], arguments=['peer']) channelOpenError = pyqtSignal([str], arguments=['message']) channelOpenSuccess = pyqtSignal([str,bool], arguments=['cid','has_backup']) @@ -156,25 +160,32 @@ class QEChannelOpener(QObject): self._finalizer.wallet = self._wallet self.finalizerChanged.emit() + @auth_protect def do_open_channel(self, funding_tx, conn_str, password): self._logger.debug('opening channel') # read funding_sat from tx; converts '!' to int value funding_sat = funding_tx.output_value_for_address(ln_dummy_address()) lnworker = self._wallet.wallet.lnworker - try: - chan, funding_tx = lnworker.open_channel( - connect_str=conn_str, - funding_tx=funding_tx, - funding_sat=funding_sat, - push_amt_sat=0, - password=password) - except Exception as e: - self._logger.exception("Problem opening channel") - self.channelOpenError.emit(_('Problem opening channel: ') + '\n' + repr(e)) - return - self._logger.debug('opening channel succeeded') - self.channelOpenSuccess.emit(chan.channel_id.hex(), chan.has_onchain_backup()) + def open_thread(): + try: + chan, _funding_tx = lnworker.open_channel( + connect_str=conn_str, + funding_tx=funding_tx, + funding_sat=funding_sat, + push_amt_sat=0, + password=password) + except Exception as e: + self._logger.exception("Problem opening channel: %s", repr(e)) + self.channelOpenError.emit(repr(e)) + return + + self._logger.debug('opening channel succeeded') + self.channelOpenSuccess.emit(chan.channel_id.hex(), chan.has_onchain_backup()) + + self._logger.debug('starting open thread') + self.channelOpening.emit(conn_str) + threading.Thread(target=open_thread).start() # TODO: it would be nice to show this before broadcasting #if chan.has_onchain_backup():