2021-04-01 19:54:53 +02:00
import QtQuick 2.6
2021-04-06 01:53:46 +02:00
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
2021-04-01 19:54:53 +02:00
import QtQml 2.6
2022-09-23 12:14:19 +02:00
import org . electrum 1.0
import "controls"
2021-04-01 19:54:53 +02:00
Item {
2022-09-23 12:14:19 +02:00
id: mainView
2021-04-06 01:53:46 +02:00
2023-03-02 12:54:59 +01:00
property string title: Daemon . currentWallet ? Daemon.currentWallet.name : qsTr ( 'no wallet loaded' )
2021-04-01 19:54:53 +02:00
2022-10-18 18:31:59 +02:00
property var _sendDialog
2023-02-27 12:20:51 +01:00
property string _intentUri
2022-10-18 18:31:59 +02:00
2023-03-18 10:56:25 +01:00
property string _request_amount
property string _request_description
property string _request_expiry
2023-03-17 23:02:43 +01:00
2022-10-04 16:33:22 +02:00
function openInvoice ( key ) {
2023-04-04 16:13:00 +02:00
invoice . key = key
var dialog = invoiceDialog . createObject ( app , { invoice: invoice } )
2022-10-04 16:33:22 +02:00
dialog . open ( )
return dialog
}
2023-02-24 14:47:28 +01:00
function openRequest ( key ) {
var dialog = receiveDialog . createObject ( app , { key: key } )
dialog . open ( )
return dialog
}
2022-10-18 18:31:59 +02:00
function openSendDialog ( ) {
_sendDialog = sendDialog . createObject ( mainView , { invoiceParser: invoiceParser } )
_sendDialog . open ( )
}
function closeSendDialog ( ) {
if ( _sendDialog ) {
_sendDialog . close ( )
_sendDialog = null
}
}
function restartSendDialog ( ) {
if ( _sendDialog ) {
_sendDialog . restart ( )
}
}
2023-03-30 12:26:28 +02:00
function showExportByTxid ( txid , helptext ) {
2023-03-31 10:49:36 +02:00
showExport ( Daemon . currentWallet . getSerializedTx ( txid ) , helptext )
2023-03-30 12:26:28 +02:00
}
2023-03-31 10:49:36 +02:00
function showExport ( data , helptext ) {
2023-03-30 12:26:28 +02:00
var dialog = exportTxDialog . createObject ( app , {
2023-03-31 10:49:36 +02:00
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.' )
2023-03-30 12:26:28 +02:00
} )
dialog . open ( )
}
2021-04-05 12:24:45 +02:00
property QtObject menu: Menu {
2022-10-31 17:17:44 +01:00
parent: Overlay . overlay
dim: true
2023-01-06 14:05:49 +01:00
modal: true
Overlay.modal: Rectangle {
2022-10-31 17:17:44 +01:00
color: "#44000000"
}
2022-03-23 13:59:46 +01:00
id: menu
2023-03-02 10:49:01 +01:00
2022-03-23 13:59:46 +01:00
MenuItem {
icon.color: 'transparent'
action: Action {
2023-03-02 10:49:01 +01:00
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' ) )
2023-03-02 10:49:01 +01:00
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 10:49:01 +01:00
}
2023-03-02 12:54:59 +01:00
}
function openPage ( url ) {
stack . pushOnRoot ( url )
2022-03-23 13:59:46 +01:00
currentIndex = - 1
}
2022-03-11 13:19:51 +01:00
}
ColumnLayout {
2023-01-27 14:54:45 +01:00
anchors.fill: parent
2023-02-23 21:49:23 +01:00
spacing: 0
2023-01-27 14:54:45 +01:00
History {
id: history
visible: Daemon . currentWallet
Layout.fillWidth: true
Layout.fillHeight: true
2022-03-11 13:19:51 +01:00
}
2023-01-27 14:54:45 +01:00
ColumnLayout {
2022-03-11 13:19:51 +01:00
Layout.alignment: Qt . AlignHCenter
2023-01-27 14:54:45 +01:00
Layout.fillHeight: true
spacing: 2 * constants . paddingXLarge
visible: ! Daemon . currentWallet
Item {
Layout.fillHeight: true
2023-01-09 18:16:07 +01:00
}
2023-01-27 14:54:45 +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
}
2022-10-04 15:09:37 +02:00
}
2022-03-11 13:19:51 +01:00
}
2023-01-27 14:54:45 +01:00
Item {
Layout.fillHeight: true
}
2022-09-23 12:14:19 +02:00
}
2023-02-03 13:33:17 +01:00
ButtonContainer {
2023-02-23 21:49:23 +01:00
id: buttonContainer
2023-02-03 13:33:17 +01:00
Layout.fillWidth: true
2022-09-23 12:14:19 +02:00
FlatButton {
2023-02-23 21:49:23 +01:00
id: receiveButton
2023-01-27 14:54:45 +01:00
visible: Daemon . currentWallet
2022-09-23 12:14:19 +02:00
Layout.fillWidth: true
Layout.preferredWidth: 1
2022-09-27 09:55:06 +02:00
icon.source: '../../icons/tab_receive.png'
2022-09-23 12:14:19 +02:00
text: qsTr ( 'Receive' )
onClicked: {
2023-03-18 10:56:25 +01:00
var dialog = receiveDetailsDialog . createObject ( mainView )
2022-09-23 12:14:19 +02:00
dialog . open ( )
2021-04-07 16:50:34 +02:00
}
2023-03-30 15:16:23 +02:00
onPressAndHold: {
2023-04-03 10:26:03 +02:00
Config . userKnowsPressAndHold = true
2023-03-31 07:56:10 +02:00
Daemon . currentWallet . delete_expired_requests ( )
2023-03-30 15:16:23 +02:00
app . stack . push ( Qt . resolvedUrl ( 'ReceiveRequests.qml' ) )
2023-04-04 16:13:00 +02:00
AppController . haptic ( )
2023-03-30 15:16:23 +02:00
}
2021-04-07 16:50:34 +02:00
}
2023-01-17 16:32:40 +01:00
FlatButton {
2023-01-27 14:54:45 +01:00
visible: Daemon . currentWallet
2023-01-17 16:32:40 +01:00
Layout.fillWidth: true
Layout.preferredWidth: 1
icon.source: '../../icons/tab_send.png'
text: qsTr ( 'Send' )
onClicked: openSendDialog ( )
2023-03-30 15:16:23 +02:00
onPressAndHold: {
2023-04-03 10:27:33 +02:00
Config . userKnowsPressAndHold = true
2023-03-30 15:16:23 +02:00
app . stack . push ( Qt . resolvedUrl ( 'Invoices.qml' ) )
2023-04-04 16:13:00 +02:00
AppController . haptic ( )
2023-03-30 15:16:23 +02:00
}
2023-01-17 16:32:40 +01:00
}
2022-09-23 12:14:19 +02:00
}
}
2021-04-01 19:54:53 +02:00
2023-04-04 16:13:00 +02:00
Invoice {
id: invoice
wallet: Daemon . currentWallet
}
2022-09-23 12:14:19 +02:00
InvoiceParser {
id: invoiceParser
wallet: Daemon . currentWallet
onValidationError: {
2022-09-27 17:10:05 +02:00
var dialog = app . messageDialog . createObject ( app , { text: message } )
2022-09-27 15:09:06 +02:00
dialog . closed . connect ( function ( ) {
2022-10-18 18:31:59 +02:00
restartSendDialog ( )
2022-09-27 15:09:06 +02:00
} )
2022-09-23 12:14:19 +02:00
dialog . open ( )
}
onValidationWarning: {
if ( code == 'no_channels' ) {
2022-09-27 17:10:05 +02:00
var dialog = app . messageDialog . createObject ( app , { text: message } )
2022-09-23 12:14:19 +02:00
dialog . open ( )
// TODO: ask user to open a channel, if funds allow
// and maybe store invoice if expiry allows
}
}
onValidationSuccess: {
2022-10-18 18:31:59 +02:00
closeSendDialog ( )
2023-04-04 16:13:00 +02:00
var dialog = invoiceDialog . createObject ( app , { invoice: invoiceParser , payImmediately: invoiceParser . isLnurlPay } )
2022-09-27 17:10:05 +02:00
dialog . open ( )
2022-09-23 12:14:19 +02:00
}
onInvoiceCreateError: console . log ( code + ' ' + message )
2021-04-07 16:50:34 +02:00
2022-09-28 18:05:45 +02:00
onLnurlRetrieved: {
2023-03-31 13:21:11 +02:00
closeSendDialog ( )
2022-09-28 18:05:45 +02:00
var dialog = lnurlPayDialog . createObject ( app , { invoiceParser: invoiceParser } )
dialog . open ( )
}
2023-03-31 13:21:11 +02:00
onLnurlError: {
var dialog = app . messageDialog . createObject ( app , { text: message } )
dialog . open ( )
}
2022-09-23 12:14:19 +02:00
}
2022-10-18 18:31:59 +02:00
Connections {
target: AppController
function onUriReceived ( uri ) {
2023-02-27 12:20:51 +01:00
console . log ( 'uri received: ' + uri )
if ( ! Daemon . currentWallet ) {
console . log ( 'No wallet open, deferring' )
_intentUri = uri
return
}
2022-10-18 18:31:59 +02:00
invoiceParser . recipient = uri
}
}
2023-02-27 12:20:51 +01:00
Connections {
target: Daemon
function onWalletLoaded ( ) {
2023-02-27 14:42:26 +01:00
if ( _intentUri ) {
2023-02-27 12:20:51 +01:00
invoiceParser . recipient = _intentUri
2023-02-27 14:42:26 +01:00
_intentUri = ''
}
2023-02-27 12:20:51 +01:00
}
}
2023-03-17 23:02:43 +01:00
Connections {
target: Daemon . currentWallet
function onRequestCreateSuccess ( key ) {
2023-03-18 10:56:25 +01:00
openRequest ( key )
2023-03-17 23:02:43 +01:00
}
2023-03-31 10:19:43 +02: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 ( )
}
2023-03-13 18:22:51 +01:00
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 ( )
}
2023-03-10 13:31:41 +01:00
function onBroadcastFailed ( txid , code , message ) {
var dialog = app . messageDialog . createObject ( app , {
text: message
} )
dialog . open ( )
}
}
2022-09-23 12:14:19 +02:00
Component {
id: invoiceDialog
2022-09-26 15:41:16 +02:00
InvoiceDialog {
2023-03-30 12:26:28 +02:00
id: _invoiceDialog
2022-09-27 15:09:06 +02:00
width: parent . width
height: parent . height
2022-09-26 15:41:16 +02:00
onDoPay: {
2023-04-04 16:13:00 +02:00
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 , {
2023-03-13 17:50:00 +01:00
address: invoice . address ,
2023-04-04 16:13:00 +02:00
satoshis: invoice . amountOverride . isEmpty ? invoice.amount : invoice . amountOverride ,
2023-03-13 17:50:00 +01:00
message: invoice . message
2022-09-26 15:41:16 +02:00
} )
2022-11-15 17:11:08 +01:00
var canComplete = ! Daemon . currentWallet . isWatchOnly && Daemon . currentWallet . canSignWithoutCosigner
2023-04-11 12:13:41 +02:00
dialog . accepted . connect ( function ( ) {
2022-11-15 17:11:08 +01:00
if ( ! canComplete ) {
2023-03-30 12:26:28 +02:00
if ( Daemon . currentWallet . isWatchOnly ) {
dialog . finalizer . save ( )
} else {
dialog . finalizer . signAndSave ( )
}
2022-09-26 15:41:16 +02:00
} else {
2022-11-15 17:11:08 +01:00
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' )
2023-04-04 16:13:00 +02:00
invoice . pay_lightning_invoice ( )
2021-04-07 16:50:34 +02:00
}
2021-04-06 01:53:46 +02:00
}
2023-01-13 21:26:10 +01:00
2022-09-27 15:09:06 +02:00
onClosed: destroy ( )
2023-03-30 12:26:28 +02:00
Connections {
target: Daemon . currentWallet
function onSaveTxSuccess ( txid ) {
_invoiceDialog . close ( )
}
}
2021-04-06 01:53:46 +02:00
}
2022-09-23 12:14:19 +02:00
}
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 ( )
}
2023-03-14 10:20:42 +01:00
onChannelBackupFound: {
var dialog = app . messageDialog . createObject ( app , {
text: qsTr ( 'Import Channel backup?' ) ,
yesno: true
} )
2023-04-11 10:22:48 +02:00
dialog . accepted . connect ( function ( ) {
2023-03-14 10:20:42 +01:00
Daemon . currentWallet . importChannelBackup ( data )
close ( )
} )
dialog . rejected . connect ( function ( ) {
close ( )
} )
dialog . open ( )
}
2022-09-26 15:41:16 +02:00
onClosed: destroy ( )
}
2022-09-23 12:14:19 +02:00
}
2021-04-07 16:50:34 +02:00
2023-03-18 10:56:25 +01: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 {
2023-03-18 10:56:25 +01:00
id: receiveDetailsDialog
2023-03-17 23:02:43 +01:00
ReceiveDetailsDialog {
2023-03-18 10:56:25 +01:00
id: _receiveDetailsDialog
2023-03-17 23:02:43 +01:00
width: parent . width * 0.9
anchors.centerIn: parent
onAccepted: {
console . log ( 'accepted' )
2023-03-18 10:56:25 +01:00
_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' )
}
2023-03-18 10:56:25 +01:00
onClosed: destroy ( )
2023-03-30 12:26:28 +02:00
}
2023-03-17 23:02:43 +01:00
}
2022-09-27 09:55:06 +02:00
Component {
id: receiveDialog
ReceiveDialog {
2022-09-27 11:43:28 +02:00
width: parent . width
height: parent . height
2022-09-27 09:55:06 +02:00
onClosed: destroy ( )
}
}
2022-09-23 12:14:19 +02:00
Component {
id: confirmPaymentDialog
ConfirmTxDialog {
2023-01-13 21:26:10 +01:00
id: _confirmPaymentDialog
2022-09-23 12:14:19 +02:00
title: qsTr ( 'Confirm Payment' )
finalizer: TxFinalizer {
wallet: Daemon . currentWallet
2023-01-03 22:52:09 +01:00
canRbf: true
2023-01-13 22:39:01 +01:00
onFinishedSave: {
2023-03-30 12:26:28 +02:00
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
}
2023-02-10 13:28:27 +01: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-06 01:53:46 +02:00
}
2021-04-07 16:50:34 +02:00
2022-09-23 12:14:19 +02:00
Component {
id: lightningPaymentProgressDialog
LightningPaymentProgressDialog {
onClosed: destroy ( )
2022-03-10 12:25:18 +01:00
}
}
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 {
2022-09-30 15:40:05 +02:00
width: parent . width * 2 / 3
2022-09-29 18:05:06 +02:00
anchors.centerIn: parent
onClosed: destroy ( )
}
}
2023-03-30 12:26:28 +02:00
Component {
id: exportTxDialog
ExportTxDialog {
onClosed: destroy ( )
}
}
2021-04-01 19:54:53 +02:00
}