Files
pallectrum/electrum/gui/qml/components/WalletMainView.qml

486 lines
15 KiB
QML
Raw Normal View History

import QtQuick 2.6
import QtQuick.Controls 2.3
2021-04-07 16:50:34 +02:00
import QtQuick.Layouts 1.0
2023-01-09 18:16:07 +01:00
import QtQuick.Controls.Material 2.0
import QtQml 2.6
import org.electrum 1.0
import "controls"
Item {
id: mainView
2023-03-02 12:54:59 +01:00
property string title: Daemon.currentWallet ? Daemon.currentWallet.name : qsTr('no wallet loaded')
property var _sendDialog
property string _intentUri
property string _request_amount
property string _request_description
property string _request_expiry
2023-03-17 23:02:43 +01:00
function openInvoice(key) {
invoice.key = key
var dialog = invoiceDialog.createObject(app, { invoice: invoice })
dialog.open()
return dialog
}
function openRequest(key) {
var dialog = receiveDialog.createObject(app, { key: key })
dialog.open()
return dialog
}
function openSendDialog() {
_sendDialog = sendDialog.createObject(mainView, {invoiceParser: invoiceParser})
_sendDialog.open()
}
function closeSendDialog() {
if (_sendDialog) {
_sendDialog.close()
_sendDialog = null
}
}
function restartSendDialog() {
if (_sendDialog) {
_sendDialog.restart()
}
}
function showExportByTxid(txid, helptext) {
showExport(Daemon.currentWallet.getSerializedTx(txid), helptext)
}
function showExport(data, helptext) {
var dialog = exportTxDialog.createObject(app, {
text: data[0],
text_qr: data[1],
text_help: helptext,
text_warn: data[2]
? ''
: qsTr('Warning: Some data (prev txs / "full utxos") was left out of the QR code as it would not fit. This might cause issues if signing offline. As a workaround, try exporting the tx as file or text instead.')
})
dialog.open()
}
2021-04-05 12:24:45 +02:00
property QtObject menu: Menu {
parent: Overlay.overlay
dim: true
2023-01-06 14:05:49 +01:00
modal: true
Overlay.modal: Rectangle {
color: "#44000000"
}
2022-03-23 13:59:46 +01:00
id: menu
2022-03-23 13:59:46 +01:00
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Wallet details')
2022-08-24 12:38:25 +02:00
enabled: Daemon.currentWallet
2023-03-02 12:54:59 +01:00
onTriggered: menu.openPage(Qt.resolvedUrl('WalletDetails.qml'))
icon.source: '../../icons/wallet.png'
2022-05-12 16:53:44 +02:00
}
}
MenuItem {
2022-03-23 13:59:46 +01:00
icon.color: 'transparent'
action: Action {
2023-03-02 12:54:59 +01:00
text: qsTr('Addresses');
onTriggered: menu.openPage(Qt.resolvedUrl('Addresses.qml'));
enabled: Daemon.currentWallet
icon.source: '../../icons/tab_addresses.png'
2022-03-23 13:59:46 +01:00
}
}
2022-05-10 17:11:16 +02:00
MenuItem {
2023-03-02 12:54:59 +01:00
icon.color: 'transparent'
2022-05-10 17:11:16 +02:00
action: Action {
2023-03-02 12:54:59 +01:00
text: qsTr('Channels');
enabled: Daemon.currentWallet && Daemon.currentWallet.isLightning
onTriggered: menu.openPage(Qt.resolvedUrl('Channels.qml'))
icon.source: '../../icons/lightning.png'
2022-05-10 17:11:16 +02:00
}
}
2023-03-02 12:54:59 +01:00
MenuSeparator { }
MenuItem {
icon.color: 'transparent'
action: Action {
text: qsTr('Other wallets');
onTriggered: menu.openPage(Qt.resolvedUrl('Wallets.qml'))
icon.source: '../../icons/file.png'
}
2023-03-02 12:54:59 +01:00
}
function openPage(url) {
stack.pushOnRoot(url)
2022-03-23 13:59:46 +01:00
currentIndex = -1
}
}
ColumnLayout {
anchors.fill: parent
2023-02-23 21:49:23 +01:00
spacing: 0
History {
id: history
visible: Daemon.currentWallet
Layout.fillWidth: true
Layout.fillHeight: true
}
ColumnLayout {
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
spacing: 2*constants.paddingXLarge
visible: !Daemon.currentWallet
Item {
Layout.fillHeight: true
2023-01-09 18:16:07 +01:00
}
Label {
Layout.alignment: Qt.AlignHCenter
text: qsTr('No wallet loaded')
font.pixelSize: constants.fontSizeXXLarge
}
Pane {
Layout.alignment: Qt.AlignHCenter
padding: 0
background: Rectangle {
color: Material.dialogColor
}
FlatButton {
text: qsTr('Open/Create Wallet')
icon.source: '../../icons/wallet.png'
onClicked: {
if (Daemon.availableWallets.rowCount() > 0) {
stack.push(Qt.resolvedUrl('Wallets.qml'))
} else {
var newww = app.newWalletWizard.createObject(app)
newww.walletCreated.connect(function() {
Daemon.availableWallets.reload()
// and load the new wallet
Daemon.load_wallet(newww.path, newww.wizard_data['password'])
})
newww.open()
}
2023-01-09 18:16:07 +01:00
}
}
}
Item {
Layout.fillHeight: true
}
}
ButtonContainer {
2023-02-23 21:49:23 +01:00
id: buttonContainer
Layout.fillWidth: true
FlatButton {
2023-02-23 21:49:23 +01:00
id: receiveButton
visible: Daemon.currentWallet
Layout.fillWidth: true
Layout.preferredWidth: 1
icon.source: '../../icons/tab_receive.png'
text: qsTr('Receive')
onClicked: {
var dialog = receiveDetailsDialog.createObject(mainView)
dialog.open()
2021-04-07 16:50:34 +02:00
}
onPressAndHold: {
Config.userKnowsPressAndHold = true
Daemon.currentWallet.delete_expired_requests()
app.stack.push(Qt.resolvedUrl('ReceiveRequests.qml'))
AppController.haptic()
}
2021-04-07 16:50:34 +02:00
}
FlatButton {
visible: Daemon.currentWallet
Layout.fillWidth: true
Layout.preferredWidth: 1
icon.source: '../../icons/tab_send.png'
text: qsTr('Send')
onClicked: openSendDialog()
onPressAndHold: {
2023-04-03 10:27:33 +02:00
Config.userKnowsPressAndHold = true
app.stack.push(Qt.resolvedUrl('Invoices.qml'))
AppController.haptic()
}
}
}
}
Invoice {
id: invoice
wallet: Daemon.currentWallet
}
InvoiceParser {
id: invoiceParser
wallet: Daemon.currentWallet
onValidationError: {
var dialog = app.messageDialog.createObject(app, { text: message })
dialog.closed.connect(function() {
restartSendDialog()
})
dialog.open()
}
onValidationWarning: {
if (code == 'no_channels') {
var dialog = app.messageDialog.createObject(app, { text: message })
dialog.open()
// TODO: ask user to open a channel, if funds allow
// and maybe store invoice if expiry allows
}
}
onValidationSuccess: {
closeSendDialog()
var dialog = invoiceDialog.createObject(app, { invoice: invoiceParser, payImmediately: invoiceParser.isLnurlPay })
dialog.open()
}
onInvoiceCreateError: console.log(code + ' ' + message)
2021-04-07 16:50:34 +02:00
2022-09-28 18:05:45 +02:00
onLnurlRetrieved: {
closeSendDialog()
2022-09-28 18:05:45 +02:00
var dialog = lnurlPayDialog.createObject(app, { invoiceParser: invoiceParser })
dialog.open()
}
onLnurlError: {
var dialog = app.messageDialog.createObject(app, { text: message })
dialog.open()
}
}
Connections {
target: AppController
function onUriReceived(uri) {
console.log('uri received: ' + uri)
if (!Daemon.currentWallet) {
console.log('No wallet open, deferring')
_intentUri = uri
return
}
invoiceParser.recipient = uri
}
}
Connections {
target: Daemon
function onWalletLoaded() {
if (_intentUri) {
invoiceParser.recipient = _intentUri
_intentUri = ''
}
}
}
2023-03-17 23:02:43 +01:00
Connections {
target: Daemon.currentWallet
function onRequestCreateSuccess(key) {
openRequest(key)
2023-03-17 23:02:43 +01:00
}
function onRequestCreateError(error) {
console.log(error)
var dialog = app.messageDialog.createObject(app, {text: error})
2023-03-17 23:02:43 +01:00
dialog.open()
}
function onOtpRequested() {
console.log('OTP requested')
var dialog = otpDialog.createObject(mainView)
dialog.accepted.connect(function() {
console.log('accepted ' + dialog.otpauth)
Daemon.currentWallet.finish_otp(dialog.otpauth)
})
dialog.open()
}
function onBroadcastFailed(txid, code, message) {
var dialog = app.messageDialog.createObject(app, {
text: message
})
dialog.open()
}
}
Component {
id: invoiceDialog
2022-09-26 15:41:16 +02:00
InvoiceDialog {
id: _invoiceDialog
width: parent.width
height: parent.height
2022-09-26 15:41:16 +02:00
onDoPay: {
if (invoice.invoiceType == Invoice.OnchainInvoice
|| (invoice.invoiceType == Invoice.LightningInvoice
&& invoice.amountOverride.isEmpty
? invoice.amount.satsInt > Daemon.currentWallet.lightningCanSend
: invoice.amountOverride.satsInt > Daemon.currentWallet.lightningCanSend
))
{
2022-09-26 15:41:16 +02:00
var dialog = confirmPaymentDialog.createObject(mainView, {
address: invoice.address,
satoshis: invoice.amountOverride.isEmpty ? invoice.amount : invoice.amountOverride,
message: invoice.message
2022-09-26 15:41:16 +02:00
})
var canComplete = !Daemon.currentWallet.isWatchOnly && Daemon.currentWallet.canSignWithoutCosigner
dialog.accepted.connect(function() {
if (!canComplete) {
if (Daemon.currentWallet.isWatchOnly) {
dialog.finalizer.save()
} else {
dialog.finalizer.signAndSave()
}
2022-09-26 15:41:16 +02:00
} else {
dialog.finalizer.signAndSend()
2022-09-26 15:41:16 +02:00
}
})
dialog.open()
} else if (invoice.invoiceType == Invoice.LightningInvoice) {
console.log('About to pay lightning invoice')
invoice.pay_lightning_invoice()
2021-04-07 16:50:34 +02:00
}
}
2023-01-13 21:26:10 +01:00
onClosed: destroy()
Connections {
target: Daemon.currentWallet
function onSaveTxSuccess(txid) {
_invoiceDialog.close()
}
}
}
}
2022-09-26 15:41:16 +02:00
Component {
id: sendDialog
SendDialog {
2022-09-27 11:43:28 +02:00
width: parent.width
height: parent.height
2022-10-27 23:12:39 +02:00
onTxFound: {
app.stack.push(Qt.resolvedUrl('TxDetails.qml'), { rawtx: data })
close()
}
onChannelBackupFound: {
var dialog = app.messageDialog.createObject(app, {
text: qsTr('Import Channel backup?'),
yesno: true
})
dialog.accepted.connect(function() {
Daemon.currentWallet.importChannelBackup(data)
close()
})
dialog.rejected.connect(function() {
close()
})
dialog.open()
}
2022-09-26 15:41:16 +02:00
onClosed: destroy()
}
}
2021-04-07 16:50:34 +02:00
function createRequest(lightning_only, reuse_address) {
var qamt = Config.unitsToSats(_request_amount)
Daemon.currentWallet.createRequest(qamt, _request_description, _request_expiry, lightning_only, reuse_address)
2023-03-17 23:02:43 +01:00
}
Component {
id: receiveDetailsDialog
2023-03-17 23:02:43 +01:00
ReceiveDetailsDialog {
id: _receiveDetailsDialog
2023-03-17 23:02:43 +01:00
width: parent.width * 0.9
anchors.centerIn: parent
onAccepted: {
console.log('accepted')
_request_amount = _receiveDetailsDialog.amount
_request_description = _receiveDetailsDialog.description
_request_expiry = _receiveDetailsDialog.expiry
createRequest(false, false)
2023-03-17 23:02:43 +01:00
}
onRejected: {
console.log('rejected')
}
onClosed: destroy()
}
2023-03-17 23:02:43 +01:00
}
Component {
id: receiveDialog
ReceiveDialog {
2022-09-27 11:43:28 +02:00
width: parent.width
height: parent.height
onClosed: destroy()
}
}
Component {
id: confirmPaymentDialog
ConfirmTxDialog {
2023-01-13 21:26:10 +01:00
id: _confirmPaymentDialog
title: qsTr('Confirm Payment')
finalizer: TxFinalizer {
wallet: Daemon.currentWallet
canRbf: true
2023-01-13 22:39:01 +01:00
onFinishedSave: {
if (wallet.isWatchOnly) {
// tx was saved. Show QR for signer(s)
showExportByTxid(txid, qsTr('Transaction created. Present this QR code to the signing device'))
} else {
// tx was (partially) signed and saved. Show QR for co-signers or online wallet
showExportByTxid(txid, qsTr('Transaction created and partially signed by this wallet. Present this QR code to the next co-signer'))
}
2023-01-13 22:39:01 +01:00
_confirmPaymentDialog.destroy()
}
2022-06-14 11:54:32 +02:00
}
// 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()
2022-06-14 11:54:32 +02:00
}
}
2021-04-07 16:50:34 +02:00
Component {
id: lightningPaymentProgressDialog
LightningPaymentProgressDialog {
onClosed: destroy()
}
}
2022-09-28 18:05:45 +02:00
Component {
id: lnurlPayDialog
LnurlPayRequestDialog {
width: parent.width * 0.9
anchors.centerIn: parent
onClosed: destroy()
}
}
2022-09-29 18:05:06 +02:00
Component {
id: otpDialog
OtpDialog {
width: parent.width * 2/3
2022-09-29 18:05:06 +02:00
anchors.centerIn: parent
onClosed: destroy()
}
}
Component {
id: exportTxDialog
ExportTxDialog {
onClosed: destroy()
}
}
}