2019-04-23 02:21:01 +00:00
// Copyright (c) 2014-2019, The Monero Project
2015-04-01 08:56:05 +00:00
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2019-04-11 01:17:29 +00:00
import QtQuick 2.9
2020-01-22 16:32:19 +00:00
import QtQuick . Controls 1.4
2016-12-17 15:57:42 +00:00
import QtQuick . Layouts 1.1
import QtQuick . Dialogs 1.2
2017-12-06 13:09:37 +00:00
import moneroComponents . Clipboard 1.0
2016-10-02 18:40:40 +00:00
import moneroComponents . PendingTransaction 1.0
2016-11-26 14:47:25 +00:00
import moneroComponents . Wallet 1.0
2019-02-20 05:29:44 +00:00
import moneroComponents . NetworkType 1.0
2019-04-23 02:21:01 +00:00
import FontAwesome 1.0
2017-11-25 21:53:07 +00:00
import "../components"
2018-08-05 20:54:11 +00:00
import "../components" as MoneroComponents
2017-11-19 20:14:05 +00:00
import "." 1.0
2018-12-19 21:37:52 +00:00
import "../js/TxUtils.js" as TxUtils
2020-01-22 16:32:19 +00:00
import "../js/Utils.js" as Utils
2014-07-07 17:08:30 +00:00
2016-06-27 12:45:48 +00:00
2014-07-07 17:08:30 +00:00
Rectangle {
2016-08-23 13:07:52 +00:00
id: root
2016-11-06 12:33:17 +00:00
signal paymentClicked ( string address , string paymentId , string amount , int mixinCount ,
2016-11-05 23:19:28 +00:00
int priority , string description )
2016-11-08 20:23:50 +00:00
signal sweepUnmixableClicked ( )
2016-06-16 14:13:46 +00:00
2017-11-25 21:53:07 +00:00
color: "transparent"
2019-05-07 05:19:40 +00:00
property alias transferHeight1: pageRoot . height
property alias transferHeight2: advancedLayout . height
2018-10-07 00:14:23 +00:00
property int mixin: 10 // (ring size 11)
2018-08-05 20:54:11 +00:00
property string warningContent: ""
2020-01-15 13:37:45 +00:00
property string sendButtonWarning: {
// Currently opened wallet is not view-only
if ( appWindow . viewOnly ) {
2020-04-21 18:57:55 +00:00
return qsTr ( "Wallet is view-only and sends are only possible by using offline transaction signing. " +
"Unless key images are imported, the balance reflects only incoming but not outgoing transactions." ) + translationManager . emptyString ;
2020-01-15 13:37:45 +00:00
}
// There are sufficient unlocked funds available
if ( walletManager . amountFromString ( amountLine . text ) > appWindow . getUnlockedBalance ( ) ) {
return qsTr ( "Amount is more than unlocked balance." ) + translationManager . emptyString ;
}
if ( addressLine . text )
{
// Address is valid
if ( ! TxUtils . checkAddress ( addressLine . text , appWindow . persistentSettings . nettype ) ) {
return qsTr ( "Address is invalid." ) + translationManager . emptyString ;
}
// Amount is nonzero
if ( ! amountLine . text || parseFloat ( amountLine . text ) <= 0 ) {
return qsTr ( "Enter an amount." ) + translationManager . emptyString ;
}
}
return "" ;
}
2019-12-08 21:06:31 +00:00
property string startLinkText: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style><a href='#'>(%1)</a>" . arg ( qsTr ( "Start daemon" ) ) + translationManager . emptyString
2019-09-28 01:46:22 +00:00
property bool warningLongPidDescription: descriptionLine . text . match ( /^[0-9a-f]{64}$/i )
2014-07-10 22:18:36 +00:00
2017-12-06 13:09:37 +00:00
Clipboard { id: clipboard }
2017-11-25 21:53:07 +00:00
2016-12-17 15:57:42 +00:00
function oa_message ( text ) {
oaPopup . title = qsTr ( "OpenAlias error" ) + translationManager . emptyString
oaPopup . text = text
oaPopup . icon = StandardIcon . Information
oaPopup . onCloseCallback = null
oaPopup . open ( )
}
2021-01-06 15:40:47 +00:00
function fillPaymentDetails ( address , payment_id , amount , tx_description , recipient_name ) {
2017-01-31 04:36:08 +00:00
addressLine . text = address
2018-12-21 16:32:26 +00:00
setPaymentId ( payment_id ) ;
2017-01-31 04:36:08 +00:00
amountLine . text = amount
2020-12-24 10:07:08 +00:00
setDescription ( ( recipient_name ? recipient_name + " " : "" ) + tx_description ) ;
2021-01-06 15:40:47 +00:00
}
function updateFromQrCode ( address , payment_id , amount , tx_description , recipient_name ) {
console . log ( "updateFromQrCode" )
fillPaymentDetails ( address , payment_id , amount , tx_description , recipient_name ) ;
2017-01-31 04:36:08 +00:00
cameraUi . qrcode_decoded . disconnect ( updateFromQrCode )
}
2018-12-21 16:32:26 +00:00
function setDescription ( value ) {
descriptionLine . text = value ;
descriptionCheckbox . checked = descriptionLine . text != "" ;
}
function setPaymentId ( value ) {
paymentIdLine . text = value ;
paymentIdCheckbox . checked = paymentIdLine . text != "" ;
}
2017-02-04 13:44:30 +00:00
function clearFields ( ) {
addressLine . text = ""
2018-12-21 16:32:26 +00:00
setPaymentId ( "" ) ;
2017-02-04 13:44:30 +00:00
amountLine . text = ""
2018-12-21 16:32:26 +00:00
setDescription ( "" ) ;
2018-12-14 03:53:50 +00:00
priorityDropdown . currentIndex = 0
2017-02-04 13:44:30 +00:00
}
2016-12-17 15:57:42 +00:00
// Information dialog
StandardDialog {
// dynamically change onclose handler
property var onCloseCallback
id: oaPopup
cancelVisible: false
onAccepted: {
if ( onCloseCallback ) {
onCloseCallback ( )
}
}
}
2016-06-16 14:13:46 +00:00
2017-08-08 09:10:44 +00:00
ColumnLayout {
2016-12-10 19:56:35 +00:00
id: pageRoot
2019-09-05 22:11:12 +00:00
anchors.margins: 20
2019-04-25 19:09:23 +00:00
anchors.topMargin: 40
2017-12-06 13:09:37 +00:00
2017-01-12 19:53:27 +00:00
anchors.left: parent . left
2017-12-06 13:09:37 +00:00
anchors.top: parent . top
2017-01-12 19:53:27 +00:00
anchors.right: parent . right
2017-08-08 09:10:44 +00:00
2019-04-25 19:09:23 +00:00
spacing: 30
2017-11-19 20:14:05 +00:00
2018-08-05 20:54:11 +00:00
RowLayout {
visible: root . warningContent !== ""
2018-03-22 04:07:09 +00:00
2018-08-05 20:54:11 +00:00
MoneroComponents . WarningBox {
text: warningContent
onLinkActivated: {
appWindow . startDaemon ( appWindow . persistentSettings . daemonFlags ) ;
2018-03-22 04:07:09 +00:00
}
}
}
2019-09-06 13:52:45 +00:00
RowLayout {
visible: leftPanel . minutesToUnlock !== ""
MoneroComponents . WarningBox {
text: qsTr ( "Spendable funds: %1 XMR. Please wait ~%2 minutes for your whole balance to become spendable." ) . arg ( leftPanel . balanceUnlockedString ) . arg ( leftPanel . minutesToUnlock )
}
}
2020-04-18 17:25:45 +00:00
// recipient address input
RowLayout {
id: addressLineRow
Layout.fillWidth: true
LineEditMulti {
id: addressLine
2020-12-02 23:55:42 +00:00
KeyNavigation.tab: amountLine
2020-04-18 17:25:45 +00:00
spacing: 0
fontBold: true
labelText: qsTr ( "Address" ) + translationManager . emptyString
labelButtonText: qsTr ( "Resolve" ) + translationManager . emptyString
placeholderText: {
if ( persistentSettings . nettype == NetworkType . MAINNET ) {
return "4.. / 8.. / OpenAlias" ;
} else if ( persistentSettings . nettype == NetworkType . STAGENET ) {
return "5.. / 7.." ;
} else if ( persistentSettings . nettype == NetworkType . TESTNET ) {
return "9.. / B.." ;
}
}
wrapMode: Text . WrapAnywhere
addressValidation: true
onTextChanged: {
const parsed = walletManager . parse_uri_to_object ( text ) ;
if ( ! parsed . error ) {
addressLine . text = parsed . address ;
setPaymentId ( parsed . payment_id ) ;
amountLine . text = parsed . amount ;
setDescription ( parsed . tx_description ) ;
}
}
2020-12-24 08:05:14 +00:00
2021-01-06 15:40:47 +00:00
MoneroComponents . InlineButton {
fontFamily: FontAwesome . fontFamily
fontPixelSize: 18
text: FontAwesome . desktop
onClicked: {
clearFields ( ) ;
const codes = oshelper . grabQrCodesFromScreen ( ) ;
for ( var index = 0 ; index < codes . length ; ++ index ) {
const parsed = walletManager . parse_uri_to_object ( codes [ index ] ) ;
if ( ! parsed . error ) {
fillPaymentDetails ( parsed . address , parsed . payment_id , parsed . amount , parsed . tx_description , parsed . recipient_name ) ;
break ;
}
}
}
}
2020-12-24 08:05:14 +00:00
MoneroComponents . InlineButton {
fontFamily: FontAwesome . fontFamily
text: FontAwesome . addressBook
onClicked: {
middlePanel . addressBookView . selectAndSend = true ;
appWindow . showPageRequest ( "AddressBook" ) ;
}
}
MoneroComponents . InlineButton {
fontFamily: FontAwesome . fontFamily
text: FontAwesome . qrcode
visible: appWindow . qrScannerEnabled
onClicked: {
cameraUi . state = "Capture"
cameraUi . qrcode_decoded . connect ( updateFromQrCode )
}
}
2020-04-18 17:25:45 +00:00
}
}
StandardButton {
id: resolveButton
width: 80
text: qsTr ( "Resolve" ) + translationManager . emptyString
visible: TxUtils . isValidOpenAliasAddress ( addressLine . text )
enabled : visible
onClicked: {
var result = walletManager . resolveOpenAlias ( addressLine . text )
if ( result ) {
var parts = result . split ( "|" )
if ( parts . length == 2 ) {
var address_ok = walletManager . addressValid ( parts [ 1 ] , appWindow . persistentSettings . nettype )
if ( parts [ 0 ] === "true" ) {
if ( address_ok ) {
// prepend openalias to description
descriptionLine . text = descriptionLine . text ? addressLine . text + " " + descriptionLine.text : addressLine . text
descriptionCheckbox . checked = true
addressLine . text = parts [ 1 ]
}
else
oa_message ( qsTr ( "No valid address found at this OpenAlias address" ) )
}
else if ( parts [ 0 ] === "false" ) {
if ( address_ok ) {
addressLine . text = parts [ 1 ]
oa_message ( qsTr ( "Address found, but the DNSSEC signatures could not be verified, so this address may be spoofed" ) )
}
else
{
oa_message ( qsTr ( "No valid address found at this OpenAlias address, but the DNSSEC signatures could not be verified, so this may be spoofed" ) )
}
}
else {
oa_message ( qsTr ( "Internal error" ) )
}
}
else {
oa_message ( qsTr ( "Internal error" ) )
}
}
else {
oa_message ( qsTr ( "No address found" ) )
}
}
}
2017-08-08 09:10:44 +00:00
GridLayout {
2019-09-05 22:11:12 +00:00
columns: appWindow . walletMode < 2 ? 1 : 2
2017-08-08 09:10:44 +00:00
Layout.fillWidth: true
2017-12-08 23:01:10 +00:00
columnSpacing: 32
2017-12-06 13:09:37 +00:00
2017-08-08 09:10:44 +00:00
ColumnLayout {
Layout.fillWidth: true
2019-04-25 19:09:23 +00:00
Layout.minimumWidth: 200
2017-02-27 21:05:28 +00:00
2018-12-28 15:34:53 +00:00
// Amount input
LineEdit {
id: amountLine
2020-12-02 23:55:42 +00:00
KeyNavigation.tab: sendButton
2018-03-22 04:07:09 +00:00
Layout.fillWidth: true
2018-12-28 15:34:53 +00:00
inlineIcon: true
2019-12-08 21:06:31 +00:00
labelText: " < style type = 'text/css' > a { text - decoration: none ; color: # 858585 ; font - size: 14 px ; } < / s t y l e > \
% 1 < a href = '#' > ( % 2 ) < / a > " . a r g ( q s T r ( " A m o u n t " ) ) . a r g ( q s T r ( " C h a n g e a c c o u n t " ) )
2019-01-14 12:25:59 +00:00
+ translationManager . emptyString
2019-11-24 23:57:59 +00:00
copyButton: ! isNaN ( amountLine . text ) && persistentSettings . fiatPriceEnabled
2020-01-22 16:32:19 +00:00
copyButtonText: "~%1 %2" . arg ( fiatApiConvertToFiat ( amountLine . text ) ) . arg ( fiatApiCurrencySymbol ( ) )
2019-11-13 08:02:19 +00:00
copyButtonEnabled: false
2019-01-14 12:25:59 +00:00
onLabelLinkActivated: {
middlePanel . accountView . selectAndSend = true ;
appWindow . showPageRequest ( "Account" )
}
2019-02-25 20:17:54 +00:00
placeholderText: "0.00"
2019-04-25 19:09:23 +00:00
width: 100
2018-12-28 15:34:53 +00:00
fontBold: true
onTextChanged: {
2020-12-14 11:57:24 +00:00
amountLine . text = amountLine . text . trim ( ) . replace ( "," , "." ) ;
2019-12-05 10:13:29 +00:00
const match = amountLine . text . match ( /^0+(\d.*)/ ) ;
if ( match ) {
const cursorPosition = amountLine . cursorPosition ;
amountLine . text = match [ 1 ] ;
amountLine . cursorPosition = Math . max ( cursorPosition , 1 ) - 1 ;
} else if ( amountLine . text . indexOf ( '.' ) === 0 ) {
amountLine . text = '0' + amountLine . text ;
if ( amountLine . text . length > 2 ) {
amountLine . cursorPosition = 1 ;
}
}
2019-12-19 00:16:00 +00:00
amountLine . error = walletManager . amountFromString ( amountLine . text ) > appWindow . getUnlockedBalance ( )
2018-12-28 15:34:53 +00:00
}
validator: RegExpValidator {
2020-12-14 11:57:24 +00:00
regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/
2017-08-08 09:10:44 +00:00
}
2020-12-24 08:05:14 +00:00
MoneroComponents . InlineButton {
text: qsTr ( "All" ) + translationManager . emptyString
onClicked: amountLine . text = "(all)"
}
2017-02-27 21:05:28 +00:00
}
2020-01-22 16:32:19 +00:00
MoneroComponents . TextPlain {
id: feeLabel
Layout.alignment: Qt . AlignRight
Layout.topMargin: 12
font.family: MoneroComponents . Style . fontRegular . name
font.pixelSize: 14
color: MoneroComponents . Style . defaultFontColor
property bool estimating: false
property var estimatedFee: null
property string estimatedFeeFiat: {
if ( ! persistentSettings . fiatPriceEnabled || estimatedFee == null ) {
return "" ;
}
const fiatFee = fiatApiConvertToFiat ( estimatedFee ) ;
return " (%1 %3)" . arg ( fiatFee < 0.01 ? "<0.01" : "~" + fiatFee ) . arg ( fiatApiCurrencySymbol ( ) ) ;
}
property var fee: {
estimatedFee = null ;
estimating = sendButton . enabled ;
2020-04-29 22:33:08 +00:00
if ( ! sendButton . enabled || ! currentWallet ) {
2020-01-22 16:32:19 +00:00
return ;
}
currentWallet . estimateTransactionFeeAsync (
addressLine . text ,
walletManager . amountFromString ( amountLine . text ) ,
priorityModelV5 . get ( priorityDropdown . currentIndex ) . priority ,
function ( amount ) {
estimatedFee = Utils . removeTrailingZeros ( amount ) ;
estimating = false ;
} ) ;
}
text: {
if ( ! sendButton . enabled || estimatedFee == null ) {
return ""
}
return "%1: ~%2 XMR" . arg ( qsTr ( "Fee" ) ) . arg ( estimatedFee ) +
estimatedFeeFiat +
translationManager . emptyString ;
}
BusyIndicator {
anchors.right: parent . right
running: feeLabel . estimating
height: parent . height
}
}
2017-08-08 09:10:44 +00:00
}
2017-02-27 21:05:28 +00:00
2017-08-08 09:10:44 +00:00
ColumnLayout {
2019-02-25 01:45:06 +00:00
visible: appWindow . walletMode >= 2
2020-01-22 16:32:19 +00:00
Layout.alignment: Qt . AlignTop
2017-08-08 09:10:44 +00:00
Label {
id: transactionPriority
2020-01-22 16:32:19 +00:00
Layout.topMargin: 0
2017-08-08 09:10:44 +00:00
text: qsTr ( "Transaction priority" ) + translationManager . emptyString
2018-01-27 23:14:42 +00:00
fontBold: false
2019-04-25 19:09:23 +00:00
fontSize: 16
2017-08-08 09:10:44 +00:00
}
// Note: workaround for translations in listElements
// ListElement: cannot use script for property value, so
// code like this wont work:
// ListElement { column1: qsTr("LOW") + translationManager.emptyString ; column2: ""; priority: PendingTransaction.Priority_Low }
// For translations to work, the strings need to be listed in
// the file components/StandardDropdown.qml too.
2017-11-25 21:53:07 +00:00
2017-08-08 09:10:44 +00:00
// Priorites after v5
ListModel {
id: priorityModelV5
2018-07-16 14:55:40 +00:00
ListElement { column1: qsTr ( "Automatic" ) ; column2: "" ; priority: 0 }
2019-10-13 17:07:52 +00:00
ListElement { column1: qsTr ( "Slow (x0.2 fee)" ) ; column2: "" ; priority: 1 }
2018-01-17 03:03:27 +00:00
ListElement { column1: qsTr ( "Normal (x1 fee)" ) ; column2: "" ; priority: 2 }
2017-08-08 09:10:44 +00:00
ListElement { column1: qsTr ( "Fast (x5 fee)" ) ; column2: "" ; priority: 3 }
2019-10-13 17:07:52 +00:00
ListElement { column1: qsTr ( "Fastest (x200 fee)" ) ; column2: "" ; priority: 4 }
2017-08-08 09:10:44 +00:00
}
StandardDropdown {
2020-01-22 16:32:19 +00:00
Layout.preferredWidth: 200
2017-08-08 09:10:44 +00:00
id: priorityDropdown
2019-04-25 19:09:23 +00:00
Layout.topMargin: 5
2018-12-14 03:53:50 +00:00
currentIndex: 0
2020-05-01 12:22:49 +00:00
dataModel: priorityModelV5
2017-08-08 09:10:44 +00:00
}
}
2017-02-27 21:05:28 +00:00
}
2019-09-28 01:46:22 +00:00
MoneroComponents . WarningBox {
text: qsTr ( " Description field contents match long payment ID format . \
Please don ' t paste long payment ID into description field , your funds might be lost . " ) + translationManager . emptyString ;
visible: warningLongPidDescription
}
2018-12-21 16:32:26 +00:00
ColumnLayout {
2019-04-17 05:30:17 +00:00
spacing: 15
ColumnLayout {
CheckBox {
id: descriptionCheckbox
border: false
2019-11-25 05:52:13 +00:00
checkedIcon: FontAwesome . minusCircle
uncheckedIcon: FontAwesome . plusCircle
fontAwesomeIcons: true
2019-04-17 05:30:17 +00:00
fontSize: descriptionLine . labelFontSize
iconOnTheLeft: true
Layout.fillWidth: true
text: qsTr ( "Add description" ) + translationManager . emptyString
onClicked: {
if ( ! descriptionCheckbox . checked ) {
descriptionLine . text = "" ;
}
2018-12-21 16:32:26 +00:00
}
}
2019-04-17 05:30:17 +00:00
LineEditMulti {
id: descriptionLine
placeholderText: qsTr ( "Saved to local wallet history" ) + translationManager . emptyString
Layout.fillWidth: true
visible: descriptionCheckbox . checked
}
2017-08-08 09:10:44 +00:00
}
2017-02-27 21:05:28 +00:00
2019-04-17 05:30:17 +00:00
ColumnLayout {
2019-09-28 02:00:51 +00:00
visible: paymentIdCheckbox . checked
2019-04-17 05:30:17 +00:00
CheckBox {
id: paymentIdCheckbox
border: false
2019-11-25 05:52:13 +00:00
checkedIcon: FontAwesome . minusCircle
uncheckedIcon: FontAwesome . plusCircle
fontAwesomeIcons: true
2019-04-17 05:30:17 +00:00
fontSize: paymentIdLine . labelFontSize
iconOnTheLeft: true
Layout.fillWidth: true
text: qsTr ( "Add payment ID" ) + translationManager . emptyString
onClicked: {
if ( ! paymentIdCheckbox . checked ) {
paymentIdLine . text = "" ;
}
2018-12-21 16:32:26 +00:00
}
}
2019-04-17 05:30:17 +00:00
// payment id input
LineEditMulti {
id: paymentIdLine
fontBold: true
placeholderText: qsTr ( "64 hexadecimal characters" ) + translationManager . emptyString
2019-09-28 02:00:51 +00:00
readOnly: true
2019-04-17 05:30:17 +00:00
Layout.fillWidth: true
wrapMode: Text . WrapAnywhere
addressValidation: false
visible: paymentIdCheckbox . checked
2019-12-19 12:47:19 +00:00
error: paymentIdCheckbox . checked
2019-04-16 11:25:01 +00:00
}
2017-08-08 09:10:44 +00:00
}
2017-12-06 13:09:37 +00:00
}
2017-02-27 21:05:28 +00:00
2019-02-25 06:06:37 +00:00
MoneroComponents . WarningBox {
2019-04-16 11:25:01 +00:00
id: paymentIdWarningBox
2019-09-28 02:00:51 +00:00
text: qsTr ( " Long payment IDs are obsolete . \
Long payment IDs were not encrypted on the blockchain and would harm your privacy . \
If the party you ' re sending to still requires a long payment ID , please notify them . " ) + translationManager . emptyString ;
2019-12-19 12:47:19 +00:00
visible: paymentIdCheckbox . checked || warningLongPidDescription
2019-04-16 11:25:01 +00:00
}
MoneroComponents . WarningBox {
2019-02-25 06:06:37 +00:00
id: sendButtonWarningBox
text: root . sendButtonWarning
visible: root . sendButtonWarning !== ""
}
2017-12-06 13:09:37 +00:00
RowLayout {
2017-03-08 19:54:11 +00:00
StandardButton {
id: sendButton
2020-12-02 23:55:42 +00:00
KeyNavigation.tab: addressLine
2019-04-11 01:17:29 +00:00
rightIcon: "qrc:///images/rightArrow.png"
rightIconInactive: "qrc:///images/rightArrowInactive.png"
2019-04-25 19:09:23 +00:00
Layout.topMargin: 4
2017-03-08 19:54:11 +00:00
text: qsTr ( "Send" ) + translationManager . emptyString
2020-01-15 13:37:45 +00:00
enabled: ! sendButtonWarningBox . visible && ! warningContent && addressLine . text && ! paymentIdWarningBox . visible
2017-03-08 19:54:11 +00:00
onClicked: {
console . log ( "Transfer: paymentClicked" )
2017-12-04 14:41:49 +00:00
var priority = priorityModelV5 . get ( priorityDropdown . currentIndex ) . priority
2017-03-08 19:54:11 +00:00
console . log ( "priority: " + priority )
console . log ( "amount: " + amountLine . text )
addressLine . text = addressLine . text . trim ( )
2018-12-21 16:32:26 +00:00
setPaymentId ( paymentIdLine . text . trim ( ) ) ;
2018-10-07 00:14:23 +00:00
root . paymentClicked ( addressLine . text , paymentIdLine . text , amountLine . text , root . mixin , priority , descriptionLine . text )
2017-03-08 19:54:11 +00:00
}
2017-02-27 21:05:28 +00:00
}
}
2017-03-08 19:54:11 +00:00
2019-12-19 00:16:00 +00:00
function checkInformation ( amount , address , nettype ) {
return amount . length > 0 && walletManager . amountFromString ( amountLine . text ) <= appWindow . getUnlockedBalance ( ) && TxUtils . checkAddress ( address , nettype )
2017-08-08 09:10:44 +00:00
}
2017-03-08 19:54:11 +00:00
2017-01-12 19:53:27 +00:00
} // pageRoot
2016-11-26 14:47:25 +00:00
2017-01-12 19:53:27 +00:00
ColumnLayout {
2019-05-07 05:19:40 +00:00
id: advancedLayout
2017-01-12 19:53:27 +00:00
anchors.top: pageRoot . bottom
anchors.left: parent . left
2019-09-05 22:11:12 +00:00
anchors.margins: 20
2019-04-25 19:09:23 +00:00
anchors.topMargin: 32
2020-04-16 21:55:22 +00:00
spacing: 10
2017-01-12 19:53:27 +00:00
enabled: ! viewOnly || pageRoot . enabled
RowLayout {
2019-01-14 00:02:44 +00:00
visible: appWindow . walletMode >= 2
2018-01-27 23:14:42 +00:00
CheckBox2 {
2017-02-27 21:05:28 +00:00
id: showAdvancedCheckbox
checked: persistentSettings . transferShowAdvanced
onClicked: {
persistentSettings . transferShowAdvanced = ! persistentSettings . transferShowAdvanced
}
2018-01-27 23:14:42 +00:00
text: qsTr ( "Advanced options" ) + translationManager . emptyString
2017-01-12 19:53:27 +00:00
}
}
2020-04-16 21:55:22 +00:00
AdvancedOptionsItem {
2019-01-14 00:02:44 +00:00
visible: persistentSettings . transferShowAdvanced && appWindow . walletMode >= 2
2020-04-16 21:55:22 +00:00
title: qsTr ( "Key images" ) + translationManager . emptyString
button1.text: qsTr ( "Export" ) + translationManager . emptyString
button1.enabled: ! appWindow . viewOnly
button1.onClicked: {
console . log ( "Transfer: export key images clicked" )
exportKeyImagesDialog . open ( ) ;
2017-01-12 19:53:27 +00:00
}
2020-04-16 21:55:22 +00:00
button2.text: qsTr ( "Import" ) + translationManager . emptyString
button2.enabled: appWindow . viewOnly && appWindow . isTrustedDaemon ( )
button2.onClicked: {
console . log ( "Transfer: import key images clicked" )
importKeyImagesDialog . open ( ) ;
2017-01-12 19:53:27 +00:00
}
2020-04-16 21:55:22 +00:00
helpTextLarge.text: qsTr ( "Required for view-only wallets to display the real balance" ) + translationManager . emptyString
helpTextSmall.text: {
var errorMessage = "" ;
if ( appWindow . viewOnly && ! appWindow . isTrustedDaemon ( ) ) {
errorMessage = "<p class='orange'>" + qsTr ( "* To import, you must connect to a local node or a trusted remote node" ) + "</p>" ;
2017-01-12 19:53:27 +00:00
}
2020-10-07 10:59:33 +00:00
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:" + MoneroComponents . Style . defaultFontColor +
";} p.orange{color:#ff9323;}</style>" +
2020-04-16 21:55:22 +00:00
"<p>" + qsTr ( "1. Using cold wallet, export the key images into a file" ) + "</p>" +
"<p>" + qsTr ( "2. Using view-only wallet, import the key images file" ) + "</p>" +
errorMessage + translationManager . emptyString
2017-01-12 19:53:27 +00:00
}
2020-04-16 21:55:22 +00:00
helpTextSmall.themeTransition: false
}
AdvancedOptionsItem {
visible: persistentSettings . transferShowAdvanced && appWindow . walletMode >= 2
title: qsTr ( "Offline transaction signing" ) + translationManager . emptyString
button1.text: qsTr ( "Create" ) + translationManager . emptyString
button1.enabled: appWindow . viewOnly && pageRoot . checkInformation ( amountLine . text , addressLine . text , appWindow . persistentSettings . nettype )
button1.onClicked: {
console . log ( "Transfer: saveTx Clicked" )
var priority = priorityModelV5 . get ( priorityDropdown . currentIndex ) . priority
console . log ( "priority: " + priority )
console . log ( "amount: " + amountLine . text )
addressLine . text = addressLine . text . trim ( )
setPaymentId ( paymentIdLine . text . trim ( ) ) ;
root . paymentClicked ( addressLine . text , paymentIdLine . text , amountLine . text , root . mixin , priority , descriptionLine . text )
2017-01-12 19:53:27 +00:00
}
2020-04-16 21:55:22 +00:00
button2.text: qsTr ( "Sign (offline)" ) + translationManager . emptyString
button2.enabled: ! appWindow . viewOnly
button2.onClicked: {
console . log ( "Transfer: sign tx clicked" )
signTxDialog . open ( ) ;
}
button3.text: qsTr ( "Submit" ) + translationManager . emptyString
button3.enabled: appWindow . viewOnly
button3.onClicked: {
console . log ( "Transfer: submit tx clicked" )
submitTxDialog . open ( ) ;
}
helpTextLarge.text: qsTr ( "Spend XMR from a cold (offline) wallet" ) + translationManager . emptyString
helpTextSmall.text: {
var errorMessage = "" ;
if ( appWindow . viewOnly && ! pageRoot . checkInformation ( amountLine . text , addressLine . text , appWindow . persistentSettings . nettype ) ) {
errorMessage = "<p class='orange'>" + qsTr ( "* To create a transaction file, please enter address and amount above" ) + "</p>" ;
2018-05-14 21:43:52 +00:00
}
2020-10-07 10:59:33 +00:00
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:" + MoneroComponents . Style . defaultFontColor +
";} p.orange{color:#ff9323;}</style>" +
2020-05-10 21:48:48 +00:00
"<p>" + qsTr ( "1. Using view-only wallet, export the outputs into a file" ) + "</p>" +
2020-04-16 21:55:22 +00:00
"<p>" + qsTr ( "2. Using cold wallet, import the outputs file and export the key images" ) + "</p>" +
"<p>" + qsTr ( "3. Using view-only wallet, import the key images file and create a transaction file" ) + "</p>" +
errorMessage +
"<p>" + qsTr ( "4. Using cold wallet, sign your transaction file" ) + "</p>" +
"<p>" + qsTr ( "5. Using view-only wallet, submit your signed transaction" ) + "</p>" + translationManager . emptyString
2018-05-14 21:43:52 +00:00
}
2020-04-16 21:55:22 +00:00
helpTextSmall.themeTransition: false
}
2018-05-14 21:43:52 +00:00
2020-04-16 21:55:22 +00:00
AdvancedOptionsItem {
visible: persistentSettings . transferShowAdvanced && appWindow . walletMode >= 2
title: qsTr ( "Unmixable outputs" ) + translationManager . emptyString
button1.text: qsTr ( "Sweep" ) + translationManager . emptyString
button1.enabled : pageRoot . enabled
button1.onClicked: {
console . log ( "Transfer: sweepUnmixableClicked" )
root . sweepUnmixableClicked ( )
2018-05-14 21:43:52 +00:00
}
2020-04-16 21:55:22 +00:00
helpTextLarge.text: qsTr ( "Create a transaction that spends old unmovable outputs" ) + translationManager . emptyString
2017-01-12 19:53:27 +00:00
}
}
//SignTxDialog
FileDialog {
id: signTxDialog
2017-03-09 00:28:24 +00:00
title: qsTr ( "Please choose a file" ) + translationManager . emptyString
2020-07-28 17:42:59 +00:00
folder: "file://" + appWindow . accountsDir
2017-01-12 19:53:27 +00:00
nameFilters: [ "Unsigned transfers (*)" ]
onAccepted: {
var path = walletManager . urlToLocalPath ( fileUrl ) ;
// Load the unsigned tx from file
var transaction = currentWallet . loadTxFile ( path ) ;
if ( transaction . status !== PendingTransaction . Status_Ok ) {
console . error ( "Can't load unsigned transaction: " , transaction . errorString ) ;
informationPopup . title = qsTr ( "Error" ) + translationManager . emptyString ;
informationPopup . text = qsTr ( "Can't load unsigned transaction: " ) + transaction . errorString
informationPopup . icon = StandardIcon . Critical
informationPopup . onCloseCallback = null
informationPopup . open ( ) ;
// deleting transaction object, we don't want memleaks
transaction . destroy ( ) ;
} else {
confirmationDialog . text = qsTr ( "\nNumber of transactions: " ) + transaction . txCount
for ( var i = 0 ; i < transaction . txCount ; ++ i ) {
confirmationDialog . text += qsTr ( "\nTransaction #%1" ) . arg ( i + 1 )
+ qsTr ( "\nRecipient: " ) + transaction . recipientAddress [ i ]
+ ( transaction . paymentId [ i ] == "" ? "" : qsTr ( "\n\payment ID: " ) + transaction . paymentId [ i ] )
+ qsTr ( "\nAmount: " ) + walletManager . displayAmount ( transaction . amount ( i ) )
+ qsTr ( "\nFee: " ) + walletManager . displayAmount ( transaction . fee ( i ) )
2018-05-21 22:44:38 +00:00
+ qsTr ( "\nRingsize: " ) + ( transaction . mixin ( i ) + 1 )
2017-01-12 19:53:27 +00:00
// TODO: add descriptions to unsigned_tx_set?
// + (transactionDescription === "" ? "" : (qsTr("\n\nDescription: ") + transactionDescription))
+ translationManager . emptyString
if ( i > 0 ) {
confirmationDialog . text += "\n\n"
}
}
console . log ( transaction . confirmationMessage ) ;
// Show confirmation dialog
confirmationDialog . title = qsTr ( "Confirmation" ) + translationManager . emptyString
confirmationDialog . icon = StandardIcon . Question
confirmationDialog . onAcceptedCallback = function ( ) {
transaction . sign ( path + "_signed" ) ;
transaction . destroy ( ) ;
} ;
confirmationDialog . onRejectedCallback = transaction . destroy ;
confirmationDialog . open ( )
}
}
onRejected: {
// File dialog closed
console . log ( "Canceled" )
}
}
//SignTxDialog
FileDialog {
id: submitTxDialog
2017-03-09 00:28:24 +00:00
title: qsTr ( "Please choose a file" ) + translationManager . emptyString
2020-07-28 17:42:59 +00:00
folder: "file://" + appWindow . accountsDir
2017-01-12 19:53:27 +00:00
nameFilters: [ "signed transfers (*)" ]
onAccepted: {
if ( ! currentWallet . submitTxFile ( walletManager . urlToLocalPath ( fileUrl ) ) ) {
informationPopup . title = qsTr ( "Error" ) + translationManager . emptyString ;
informationPopup . text = qsTr ( "Can't submit transaction: " ) + currentWallet . errorString
informationPopup . icon = StandardIcon . Critical
informationPopup . onCloseCallback = null
informationPopup . open ( ) ;
} else {
informationPopup . title = qsTr ( "Information" ) + translationManager . emptyString
2018-03-23 22:53:29 +00:00
informationPopup . text = qsTr ( "Monero sent successfully" ) + translationManager . emptyString
2017-01-12 19:53:27 +00:00
informationPopup . icon = StandardIcon . Information
informationPopup . onCloseCallback = null
informationPopup . open ( ) ;
}
}
onRejected: {
console . log ( "Canceled" )
}
}
2018-05-14 21:43:52 +00:00
//ExportKeyImagesDialog
FileDialog {
id: exportKeyImagesDialog
selectMultiple: false
selectExisting: false
onAccepted: {
console . log ( walletManager . urlToLocalPath ( exportKeyImagesDialog . fileUrl ) )
currentWallet . exportKeyImages ( walletManager . urlToLocalPath ( exportKeyImagesDialog . fileUrl ) ) ;
}
onRejected: {
console . log ( "Canceled" ) ;
}
}
//ImportKeyImagesDialog
FileDialog {
id: importKeyImagesDialog
selectMultiple: false
selectExisting: true
title: qsTr ( "Please choose a file" ) + translationManager . emptyString
onAccepted: {
console . log ( walletManager . urlToLocalPath ( importKeyImagesDialog . fileUrl ) )
currentWallet . importKeyImages ( walletManager . urlToLocalPath ( importKeyImagesDialog . fileUrl ) ) ;
}
onRejected: {
console . log ( "Canceled" ) ;
}
}
2016-11-26 14:47:25 +00:00
2018-03-22 04:07:09 +00:00
2016-11-26 14:47:25 +00:00
Component.onCompleted: {
//Disable password page until enabled by updateStatus
2016-12-10 19:56:35 +00:00
pageRoot . enabled = false
2016-11-26 14:47:25 +00:00
}
// fires on every page load
function onPageCompleted ( ) {
console . log ( "transfer page loaded" )
updateStatus ( ) ;
}
//TODO: Add daemon sync status
//TODO: enable send page when we're connected and daemon is synced
function updateStatus ( ) {
2019-01-14 00:02:44 +00:00
var messageNotConnected = qsTr ( "Wallet is not connected to daemon." ) ;
2019-12-16 22:48:35 +00:00
if ( appWindow . walletMode >= 2 && ! persistentSettings . useRemoteNode ) messageNotConnected += root . startLinkText ;
2018-03-22 04:07:09 +00:00
pageRoot . enabled = true ;
2016-11-26 14:47:25 +00:00
if ( typeof currentWallet === "undefined" ) {
2019-01-14 00:02:44 +00:00
root . warningContent = messageNotConnected ;
2016-11-26 14:47:25 +00:00
return ;
2017-01-04 16:25:22 +00:00
}
if ( currentWallet . viewOnly ) {
2018-03-22 04:07:09 +00:00
// warningText.text = qsTr("Wallet is view only.")
2017-01-12 19:53:27 +00:00
//return;
2016-11-26 14:47:25 +00:00
}
2018-03-22 04:07:09 +00:00
//pageRoot.enabled = false;
2016-11-26 14:47:25 +00:00
2017-02-23 18:47:58 +00:00
switch ( currentWallet . connected ( ) ) {
2020-01-28 04:43:31 +00:00
case Wallet.ConnectionStatus_Connecting:
2020-05-05 11:12:10 +00:00
root . warningContent = qsTr ( "Wallet is connecting to daemon." )
break
2016-11-26 14:47:25 +00:00
case Wallet.ConnectionStatus_Disconnected:
2019-01-14 00:02:44 +00:00
root . warningContent = messageNotConnected ;
2016-11-26 14:47:25 +00:00
break
case Wallet.ConnectionStatus_WrongVersion:
2018-08-05 20:54:11 +00:00
root . warningContent = qsTr ( "Connected daemon is not compatible with GUI. \n" +
2016-11-26 14:47:25 +00:00
"Please upgrade or connect to another daemon" )
break
default:
if ( ! appWindow . daemonSynced ) {
2019-01-14 00:02:44 +00:00
root . warningContent = qsTr ( "Waiting on daemon synchronization to finish." )
2016-11-26 14:47:25 +00:00
} else {
// everything OK, enable transfer page
2017-08-08 09:10:44 +00:00
// Light wallet is always ready
2016-12-10 19:56:35 +00:00
pageRoot . enabled = true ;
2018-08-05 20:54:11 +00:00
root . warningContent = "" ;
2016-11-26 14:47:25 +00:00
}
}
}
2016-12-10 01:01:04 +00:00
// Popuplate fields from addressbook.
2019-03-22 20:02:08 +00:00
function sendTo ( address , paymentId , description , amount ) {
middlePanel . state = 'Transfer' ;
if ( typeof address !== 'undefined' )
addressLine . text = address
if ( typeof paymentId !== 'undefined' )
setPaymentId ( paymentId ) ;
if ( typeof description !== 'undefined' )
setDescription ( description ) ;
if ( typeof amount !== 'undefined' )
amountLine . text = amount ;
2016-12-10 01:01:04 +00:00
}
2014-07-07 17:08:30 +00:00
}