From 26d9c06e0c8c89d6e68645a0112193193e440ac0 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Thu, 27 Oct 2022 23:12:39 +0200 Subject: [PATCH] qml: add export TX and scan of TX --- .../gui/qml/components/ExportTxDialog.qml | 101 ++++++++++++++++++ electrum/gui/qml/components/SendDialog.qml | 21 +++- electrum/gui/qml/components/TxDetails.qml | 24 ++++- .../gui/qml/components/WalletMainView.qml | 4 + electrum/gui/qml/qebitcoin.py | 2 +- electrum/gui/qml/qetxdetails.py | 15 ++- 6 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 electrum/gui/qml/components/ExportTxDialog.qml diff --git a/electrum/gui/qml/components/ExportTxDialog.qml b/electrum/gui/qml/components/ExportTxDialog.qml new file mode 100644 index 000000000..2cc092c27 --- /dev/null +++ b/electrum/gui/qml/components/ExportTxDialog.qml @@ -0,0 +1,101 @@ +import QtQuick 2.6 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Material 2.0 + +import "controls" + +ElDialog { + id: dialog + + property QtObject txdetails + + property string text + property string text_qr + // if text_qr is undefined text will be used + property string text_help + + title: qsTr('Export Transaction') + + parent: Overlay.overlay + modal: true + standardButtons: Dialog.Close + + width: parent.width + height: parent.height + + Overlay.modal: Rectangle { + color: "#aa000000" + } + + Flickable { + anchors.fill: parent + contentHeight: rootLayout.height + clip:true + interactive: height < contentHeight + + ColumnLayout { + id: rootLayout + width: parent.width + spacing: constants.paddingMedium + + Item { + Layout.fillWidth: true + Layout.preferredHeight: qr.height + Layout.topMargin: constants.paddingSmall + Layout.bottomMargin: constants.paddingSmall + QRImage { + id: qr + qrdata: dialog.text_qr + anchors.centerIn: parent + } + } + + Label { + visible: dialog.text_help + text: dialog.text_help + wrapMode: Text.Wrap + Layout.fillWidth: true + } + + Rectangle { + height: 1 + Layout.preferredWidth: qr.width + Layout.alignment: Qt.AlignHCenter + color: Material.accentColor + } + + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + + FlatButton { + text: qsTr('Copy') + icon.source: '../../icons/copy_bw.png' + onClicked: AppController.textToClipboard(dialog.text) + } + FlatButton { + text: qsTr('Share') + icon.source: '../../icons/share.png' + onClicked: { + AppController.doShare(dialog.text, dialog.title) + } + } + } + } + } + + Connections { + target: dialog.enter + function onRunningChanged() { + if (!dialog.enter.running) { + qr.render = true + } + } + } + + Component.onCompleted: { + text = dialog.txdetails.serializedTx(false) + text_qr = dialog.txdetails.serializedTx(true) + } +} diff --git a/electrum/gui/qml/components/SendDialog.qml b/electrum/gui/qml/components/SendDialog.qml index 9416d0355..a699db3fe 100644 --- a/electrum/gui/qml/components/SendDialog.qml +++ b/electrum/gui/qml/components/SendDialog.qml @@ -12,6 +12,8 @@ ElDialog { property InvoiceParser invoiceParser + signal txFound(data: string) + parent: Overlay.overlay modal: true standardButtons: Dialog.Close @@ -28,6 +30,15 @@ ElDialog { qrscan.restart() } + function dispatch(data) { + if (bitcoin.isRawTx(data)) { + // app.stack.push(Qt.resolvedUrl('TxDetails.qml'), { rawtx: text }) + txFound(data) + } else { + invoiceParser.recipient = data + } + } + ColumnLayout { anchors.fill: parent spacing: 0 @@ -37,7 +48,7 @@ ElDialog { Layout.preferredWidth: parent.width Layout.fillHeight: true - onFound: invoiceParser.recipient = scanData + onFound: dialog.dispatch(scanData) } FlatButton { @@ -47,7 +58,7 @@ ElDialog { onClicked: { var _mid = manualInputDialog.createObject(mainView) _mid.accepted.connect(function() { - invoiceParser.recipient = _mid.recipient + dialog.dispatch(_mid.recipient) }) _mid.open() } @@ -57,7 +68,7 @@ ElDialog { Layout.fillWidth: true icon.source: '../../icons/paste.png' text: qsTr('Paste from clipboard') - onClicked: invoiceParser.recipient = AppController.clipboardToText() + onClicked: dialog.dispatch(AppController.clipboardToText()) } } @@ -102,4 +113,8 @@ ElDialog { onClosed: destroy() } } + + Bitcoin { + id: bitcoin + } } diff --git a/electrum/gui/qml/components/TxDetails.qml b/electrum/gui/qml/components/TxDetails.qml index 556d123ab..c3a957056 100644 --- a/electrum/gui/qml/components/TxDetails.qml +++ b/electrum/gui/qml/components/TxDetails.qml @@ -27,6 +27,16 @@ Pane { property QtObject menu: Menu { id: menu + MenuItem { + icon.color: 'transparent' + action: Action { + text: qsTr('Export') + onTriggered: { + var dialog = exportTxDialog.createObject(root, { txdetails: txdetails }) + dialog.open() + } + } + } MenuItem { icon.color: 'transparent' action: Action { @@ -292,7 +302,7 @@ Pane { RowLayout { width: parent.width Label { - text: root.txid + text: txdetails.txid font.pixelSize: constants.fontSizeLarge font.family: FixedFont Layout.fillWidth: true @@ -301,10 +311,10 @@ Pane { ToolButton { icon.source: '../../icons/share.png' icon.color: 'transparent' - enabled: root.txid + enabled: txdetails.txid onClicked: { var dialog = app.genericShareDialog.createObject(root, - { title: qsTr('Transaction ID'), text: root.txid } + { title: qsTr('Transaction ID'), text: txdetails.txid } ) dialog.open() } @@ -354,7 +364,7 @@ Pane { Layout.columnSpan: 2 Button { text: qsTr('Sign') - enabled: !txdetails.isComplete + enabled: txdetails.canSign onClicked: txdetails.sign() } Button { @@ -401,4 +411,10 @@ Pane { } } + Component { + id: exportTxDialog + ExportTxDialog { + onClosed: destroy() + } + } } diff --git a/electrum/gui/qml/components/WalletMainView.qml b/electrum/gui/qml/components/WalletMainView.qml index e58e69bfd..9ac1b1491 100644 --- a/electrum/gui/qml/components/WalletMainView.qml +++ b/electrum/gui/qml/components/WalletMainView.qml @@ -278,6 +278,10 @@ Item { width: parent.width height: parent.height + onTxFound: { + app.stack.push(Qt.resolvedUrl('TxDetails.qml'), { rawtx: data }) + close() + } onClosed: destroy() } } diff --git a/electrum/gui/qml/qebitcoin.py b/electrum/gui/qml/qebitcoin.py index 70dd6437f..1d2cf6c5b 100644 --- a/electrum/gui/qml/qebitcoin.py +++ b/electrum/gui/qml/qebitcoin.py @@ -154,7 +154,7 @@ class QEBitcoin(QObject): return create_bip21_uri(address, satoshis.satsInt, message, extra_query_params=extra_params) @pyqtSlot(str, result=bool) - def verify_raw_tx(self, rawtx): + def isRawTx(self, rawtx): try: tx_from_any(rawtx) return True diff --git a/electrum/gui/qml/qetxdetails.py b/electrum/gui/qml/qetxdetails.py index 63c4c9d00..1fd15d61b 100644 --- a/electrum/gui/qml/qetxdetails.py +++ b/electrum/gui/qml/qetxdetails.py @@ -35,9 +35,9 @@ class QETxDetails(QObject): _can_cpfp = False _can_save_as_local = False _can_remove = False + _can_sign = False _is_unrelated = False _is_complete = False - _is_mined = False _mempool_depth = '' @@ -186,6 +186,10 @@ class QETxDetails(QObject): def canRemove(self): return self._can_remove + @pyqtProperty(bool, notify=detailsChanged) + def canSign(self): + return self._can_sign + @pyqtProperty(bool, notify=detailsChanged) def isUnrelated(self): return self._is_unrelated @@ -249,6 +253,7 @@ class QETxDetails(QObject): self._can_cpfp = txinfo.can_cpfp self._can_save_as_local = txinfo.can_save_as_local self._can_remove = txinfo.can_remove + self._can_sign = not self._is_complete and self._wallet.wallet.can_sign(self._tx) self.detailsChanged.emit() @@ -326,3 +331,11 @@ class QETxDetails(QObject): self._wallet.wallet.adb.remove_transaction(txid) self._wallet.wallet.save_db() + + @pyqtSlot(result=str) + @pyqtSlot(bool, result=str) + def serializedTx(self, for_qr=False): + if for_qr: + return self._tx.to_qr_data() + else: + return str(self._tx)