Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 45 additions & 4 deletions electrum/gui/qml/components/ConfirmTxDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ ElDialog {
required property var satoshis // type: Amount
property string address
property string message
property bool showOptions: true
property alias amountLabelText: amountLabel.text
property alias sendButtonText: sendButton.text

signal confirmed

title: qsTr('Transaction Fee')
iconSource: Qt.resolvedUrl('../../icons/question.png')

Expand Down Expand Up @@ -150,7 +151,7 @@ ElDialog {
Layout.columnSpan: 2
labelText: qsTr('Options')
color: Material.accentColor
visible: showOptions
visible: finalizer.txOptions
}

DialogHighlightPane {
Expand All @@ -163,9 +164,12 @@ ElDialog {
id: optionslayout
width: parent.width
columns: 2
rowSpacing: 0

ElCheckBox {
id: cb_multiple_change
Layout.fillWidth: true
visible: finalizer.txOptions & TxFinalizer.TxOptions.MULTIPLE_CHANGE
text: qsTr('Use multiple change addresses')
onCheckedChanged: {
if (activeFocus) {
Expand All @@ -179,13 +183,16 @@ ElDialog {
}

HelpButton {
visible: cb_multiple_change.visible
heading: qsTr('Use multiple change addresses')
helptext: [qsTr('In some cases, use up to 3 change addresses in order to break up large coin amounts and obfuscate the recipient address.'),
qsTr('This may result in higher transactions fees.')].join(' ')
}

ElCheckBox {
id: cb_output_rounding
Layout.fillWidth: true
visible: finalizer.txOptions & TxFinalizer.TxOptions.OUTPUT_ROUNDING
text: Config.shortDescFor('WALLET_COIN_CHOOSER_OUTPUT_ROUNDING')
onCheckedChanged: {
if (activeFocus) {
Expand All @@ -199,10 +206,44 @@ ElDialog {
}

HelpButton {
visible: cb_output_rounding.visible
heading: Config.shortDescFor('WALLET_COIN_CHOOSER_OUTPUT_ROUNDING')
helptext: Config.longDescFor('WALLET_COIN_CHOOSER_OUTPUT_ROUNDING')
}

ElCheckBox {
id: cb_send_change_to_lightning
Layout.fillWidth: true
visible: finalizer.txOptions & TxFinalizer.TxOptions.SEND_CHANGE_TO_LIGHTNING
&& Daemon.currentWallet.isLightning && Daemon.currentWallet.lightningCanReceive.satsInt > 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe using > submarine_swaps.MIN_SWAP_AMOUNT_SAT would be more reliable UX than > 0 as it is still not possible to receive change on lightning if the inbound liquidity is lower than the minimum swap amount.

text: Config.shortDescFor('WALLET_SEND_CHANGE_TO_LIGHTNING')
onCheckedChanged: {
if (activeFocus) {
Config.sendChangeToLightning = checked
finalizer.doUpdate()
}
}
Component.onCompleted: {
checked = Config.sendChangeToLightning
}
}

HelpButton {
visible: cb_send_change_to_lightning.visible
heading: Config.shortDescFor('WALLET_SEND_CHANGE_TO_LIGHTNING')
helptext: Config.longDescFor('WALLET_SEND_CHANGE_TO_LIGHTNING')
}

Label {
visible: cb_send_change_to_lightning.visible && cb_send_change_to_lightning.checked
color: constants.mutedForeground
font.pixelSize: constants.fontSizeSmall
text: finalizer.swapStatusMsg
Layout.topMargin: -constants.paddingSmall
Layout.leftMargin: cb_send_change_to_lightning.contentItem.leftPadding
+ cb_send_change_to_lightning.padding
}

}
}

Expand Down Expand Up @@ -283,8 +324,8 @@ ElDialog {
? qsTr('Finalize...')
: qsTr('Pay...')
icon.source: '../../icons/confirmed.png'
enabled: finalizer.valid
onClicked: doAccept()
enabled: finalizer.valid && !finalizer.busy
onClicked: confirmed()
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions electrum/gui/qml/components/MessageDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ ElDialog {
property bool yesno: false
property alias text: message.text
property bool richText: false
property alias buttonText: primaryButton.text
property alias buttonIcon: primaryButton.icon.source

z: 1 // raise z so it also covers dialogs using overlay as parent

Expand Down Expand Up @@ -55,6 +57,7 @@ ElDialog {
}

FlatButton {
id: primaryButton
Layout.fillWidth: true
textUnderIcon: false
text: qsTr('Ok')
Expand Down
15 changes: 11 additions & 4 deletions electrum/gui/qml/components/OpenChannelDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ ElDialog {
width: parent.width
height: parent.height

property var _openerConfirmTxDialog: null

ColumnLayout {
anchors.fill: parent
spacing: 0
Expand Down Expand Up @@ -255,14 +257,18 @@ ElDialog {
Component {
id: confirmOpenChannelDialog
ConfirmTxDialog {
id: _confirmOpenChannelDialog
amountLabelText: qsTr('Channel capacity')
sendButtonText: qsTr('Open Channel')
finalizer: channelopener.finalizer

onClosed: destroy()
}
}

ChannelOpener {
id: channelopener

wallet: Daemon.currentWallet
onAuthRequired: (method, authMessage) => {
app.handleAuthRequired(channelopener, method, authMessage)
Expand All @@ -288,13 +294,13 @@ ElDialog {
})
}
onFinalizerChanged: {
var dialog = confirmOpenChannelDialog.createObject(app, {
_openerConfirmTxDialog = confirmOpenChannelDialog.createObject(app, {
satoshis: channelopener.amount
})
dialog.accepted.connect(function() {
dialog.finalizer.signAndSend()
_openerConfirmTxDialog.confirmed.connect(function() {
_openerConfirmTxDialog.finalizer.signAndSend()
})
dialog.open()
_openerConfirmTxDialog.open()
}
onChannelOpening: (peer) => {
console.log('Channel is opening')
Expand All @@ -318,6 +324,7 @@ ElDialog {
if (!has_onchain_backup) {
app.channelOpenProgressDialog.channelBackup = channelopener.channelBackup(cid)
}
_openerConfirmTxDialog.close()
// TODO: handle incomplete TX
root.close()
}
Expand Down
59 changes: 50 additions & 9 deletions electrum/gui/qml/components/WalletMainView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,12 @@ Item {
message: invoice.message
})
var canComplete = !Daemon.currentWallet.isWatchOnly && Daemon.currentWallet.canSignWithoutCosigner
dialog.accepted.connect(function() {
dialog.confirmed.connect(function() {
if (invoice.canSave)
if (!invoice.saveInvoice())
if (!invoice.saveInvoice()) {
dialog.close()
return
}
if (!canComplete) {
if (Daemon.currentWallet.isWatchOnly) {
dialog.finalizer.saveOrShow()
Expand All @@ -142,13 +144,12 @@ Item {
var finalizerDialog = confirmSweepDialog.createObject(mainView, {
privateKeys: dialog.privateKeys,
message: qsTr('Sweep transaction'),
showOptions: false,
amountLabelText: qsTr('Total sweep amount'),
sendButtonText: Daemon.currentWallet.isWatchOnly
? qsTr('Sweep...')
: qsTr('Sweep')
})
finalizerDialog.accepted.connect(function() {
finalizerDialog.confirmed.connect(function() {
if (Daemon.currentWallet.isWatchOnly) {
var confirmdialog = app.messageDialog.createObject(mainView, {
title: qsTr('Confirm Sweep'),
Expand All @@ -164,6 +165,7 @@ Item {
}
console.log("Sending sweep transaction")
finalizerDialog.finalizer.send()
finalizerDialog.close()
})
finalizerDialog.open()
})
Expand Down Expand Up @@ -672,8 +674,13 @@ Item {
id: _confirmPaymentDialog
title: qsTr('Confirm Payment')
finalizer: TxFinalizer {
id: _txfinalizer
property var _swapwaitdialog
wallet: Daemon.currentWallet
canRbf: true
txOptions: TxFinalizer.TxOptions.MULTIPLE_CHANGE
| TxFinalizer.TxOptions.OUTPUT_ROUNDING
| TxFinalizer.TxOptions.SEND_CHANGE_TO_LIGHTNING
onFinished: (signed, saved, complete) => {
if (!complete) {
var msg
Expand All @@ -693,7 +700,7 @@ Item {
}
showExport(getSerializedTx(), msg)
}
_confirmPaymentDialog.destroy()
_confirmPaymentDialog.close()
}
onSignError: (message) => {
var dialog = app.messageDialog.createObject(mainView, {
Expand All @@ -703,12 +710,43 @@ Item {
})
dialog.open()
}
onSwapError: (message) => {
if (_swapwaitdialog)
_swapwaitdialog.close()
var dialog = app.messageDialog.createObject(mainView, {
title: qsTr('Error'),
text: [qsTr('Could not swap change'), message].join('\n\n'),
iconSource: '../../../icons/warning.png'
})
dialog.open()
}
onSwapStart: {
_swapwaitdialog = app.messageDialog.createObject(mainView, {
title: qsTr('Please wait...'),
text: [qsTr('waiting for lightning invoice'), ''].join('\n\n'),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
text: [qsTr('waiting for lightning invoice'), ''].join('\n\n'),
text: [qsTr('Requesting Submarine Swap for change...'), ''].join('\n\n'),

Might be easier to understand? It might not be clear to the user why they have to wait for a lightning invoice if they aren't aware of the swap technicalities.

Image

iconSource: Qt.resolvedUrl('../../icons/info.png'),
buttonText: qsTr('Cancel'),
buttonIcon: Qt.resolvedUrl('../../icons/closebutton.png')
})
_swapwaitdialog.accepted.connect(function() {
cancelSwap()
})
Comment on lines +731 to +733

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should clicking outside the dialog/back maybe also cancel the swap?

_swapwaitdialog.rejected.connect(function() {
    cancelSwap()
})

Alternatively we could also disallow closing the dialog by clicking outside so the user must either wait or click cancel.

_swapwaitdialog.open()
}
onSwapFunded: {
if (_swapwaitdialog)
_swapwaitdialog.close()
}
onAuthRequired: (method, authMessage) => {
app.handleAuthRequired(_txfinalizer, method, authMessage)
}
}

// TODO: lingering confirmPaymentDialogs can raise exceptions in
// the child finalizer when currentWallet disappears, but we need
// it long enough for the finalizer to finish..
// onClosed: destroy()
// NOTE: destroy-on-close was previously disabled due to the 'accept' signal implicitly
// closing the dialog, while the finalizer could still trigger a callback.
// ConfirmTxDialog now instead emits a custom 'confirmed' signal when user ok's, but
// keep in mind this requires explicit closing of the dialog afterwards
onClosed: destroy()
}
}

Expand All @@ -724,7 +762,10 @@ Item {
wallet: Daemon.currentWallet
canRbf: true
privateKeys: _confirmSweepDialog.privateKeys
txOptions: TxFinalizer.TxOptions.NONE
}

onClosed: destroy()
}
}

Expand Down
1 change: 1 addition & 0 deletions electrum/gui/qml/qechannelopener.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def openChannel(self, confirm_backup_conflict=False):

self._finalizer = QETxFinalizer(self, make_tx=mktx, accept=acpt)
self._finalizer.canRbf = False
self._finalizer.txOptions = QETxFinalizer.TxOptions.MULTIPLE_CHANGE | QETxFinalizer.TxOptions.OUTPUT_ROUNDING
self._finalizer.amount = self._amount
self._finalizer.wallet = self._wallet
self.finalizerChanged.emit()
Expand Down
11 changes: 11 additions & 0 deletions electrum/gui/qml/qeconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,17 @@ def walletDidUseSinglePassword(self):
# TODO: consider removing once encrypted wallet file headers are available
return self.config.WALLET_DID_USE_SINGLE_PASSWORD

sendChangeToLightningChanged = pyqtSignal()
@pyqtProperty(bool, notify=sendChangeToLightningChanged)
def sendChangeToLightning(self):
return self.config.WALLET_SEND_CHANGE_TO_LIGHTNING

@sendChangeToLightning.setter
def sendChangeToLightning(self, sendChangeToLightning):
if sendChangeToLightning != self.config.WALLET_SEND_CHANGE_TO_LIGHTNING:
self.config.WALLET_SEND_CHANGE_TO_LIGHTNING = sendChangeToLightning
self.sendChangeToLightningChanged.emit()

@pyqtSlot('qint64', result=str)
@pyqtSlot(QEAmount, result=str)
def formatSatsForEditing(self, satoshis):
Expand Down
Loading