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.
2021-02-05 15:28:01 +00:00
import QtQml . Models 2.2
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
2021-02-02 16:00:46 +00:00
signal paymentClicked ( var recipients , string paymentId , int mixinCount , 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
2021-02-05 15:28:01 +00:00
if ( recipientModel . getAmountTotal ( ) > appWindow . getUnlockedBalance ( ) ) {
2020-01-15 13:37:45 +00:00
return qsTr ( "Amount is more than unlocked balance." ) + translationManager . emptyString ;
}
2021-02-05 15:28:01 +00:00
if ( ! recipientModel . hasEmptyAddress ( ) ) {
2020-01-15 13:37:45 +00:00
// Address is valid
2021-02-05 15:28:01 +00:00
if ( recipientModel . hasInvalidAddress ( ) ) {
2020-01-15 13:37:45 +00:00
return qsTr ( "Address is invalid." ) + translationManager . emptyString ;
}
// Amount is nonzero
2021-02-05 15:28:01 +00:00
if ( recipientModel . hasEmptyAmount ( ) ) {
2020-01-15 13:37:45 +00:00
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 ) {
2021-02-05 15:28:01 +00:00
if ( recipientModel . count > 0 ) {
const last = recipientModel . count - 1 ;
if ( recipientModel . get ( recipientModel . count - 1 ) . address == "" ) {
recipientModel . remove ( last ) ;
}
}
recipientModel . newRecipient ( address , Utils . removeTrailingZeros ( amount || "" ) ) ;
setPaymentId ( payment_id || "" ) ;
2021-07-31 08:42:13 +00:00
setDescription ( ( recipient_name ? recipient_name + ( tx_description ? " (" + tx_description + ")" : "" ) : ( 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 ( ) {
2021-02-05 15:28:01 +00:00
recipientModel . clear ( ) ;
fillPaymentDetails ( "" , "" , "" , "" , "" ) ;
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 )
}
}
2021-02-05 15:28:01 +00:00
ListModel {
id: recipientModel
2020-12-24 08:05:14 +00:00
2021-02-05 15:28:01 +00:00
readonly property int maxRecipients: 16
ListElement {
address: ""
amount: ""
}
function newRecipient ( address , amount ) {
if ( recipientModel . count < maxRecipients ) {
recipientModel . append ( { address: address , amount: amount } ) ;
return true ;
}
return false ;
}
function getRecipients ( ) {
var recipients = [ ] ;
for ( var index = 0 ; index < recipientModel . count ; ++ index ) {
const recipient = recipientModel . get ( index ) ;
recipients . push ( {
address: recipient . address ,
amount: recipient . amount ,
} ) ;
}
return recipients ;
}
function getAmountTotal ( ) {
var sum = [ ] ;
for ( var index = 0 ; index < recipientModel . count ; ++ index ) {
const amount = recipientModel . get ( index ) . amount ;
if ( amount == "(all)" ) {
return appWindow . getUnlockedBalance ( ) ;
2021-01-06 15:40:47 +00:00
}
2021-02-05 15:28:01 +00:00
sum . push ( amount || "0" ) ;
2021-01-06 15:40:47 +00:00
}
2021-02-05 15:28:01 +00:00
return walletManager . amountsSumFromStrings ( sum ) ;
}
2021-01-06 15:40:47 +00:00
2021-02-05 15:28:01 +00:00
function hasEmptyAmount ( ) {
for ( var index = 0 ; index < recipientModel . count ; ++ index ) {
if ( recipientModel . get ( index ) . amount === "" ) {
return true ;
2020-12-24 08:05:14 +00:00
}
}
2021-02-05 15:28:01 +00:00
return false ;
}
2020-12-24 08:05:14 +00:00
2021-02-05 15:28:01 +00:00
function hasEmptyAddress ( ) {
for ( var index = 0 ; index < recipientModel . count ; ++ index ) {
if ( recipientModel . get ( index ) . address === "" ) {
return true ;
2020-12-24 08:05:14 +00:00
}
}
2021-02-05 15:28:01 +00:00
return false ;
}
2020-04-18 17:25:45 +00:00
2021-02-05 15:28:01 +00:00
function hasInvalidAddress ( ) {
for ( var index = 0 ; index < recipientModel . count ; ++ index ) {
if ( ! TxUtils . checkAddress ( recipientModel . get ( index ) . address , appWindow . persistentSettings . nettype ) ) {
return true ;
}
}
return false ;
}
}
Item {
Layout.fillWidth: true
implicitHeight: recipientLayout . height
ColumnLayout {
id: recipientLayout
anchors.left: parent . left
anchors.right: parent . right
spacing: 0
readonly property int colSpacing: 10
readonly property int rowSpacing: 10
readonly property int secondRowWidth: 125
readonly property int thirdRowWidth: 50
RowLayout {
Layout.bottomMargin: recipientLayout . rowSpacing / 2
spacing: recipientLayout . colSpacing
RowLayout {
id: addressLabel
spacing: 6
Layout.fillWidth: true
MoneroComponents . TextPlain {
font.family: MoneroComponents . Style . fontRegular . name
font.pixelSize: 16
color: MoneroComponents . Style . defaultFontColor
text: qsTr ( "Address" ) + translationManager . emptyString
}
MoneroComponents . InlineButton {
2021-04-14 19:30:42 +00:00
fontFamily: FontAwesome . fontFamilySolid
fontStyleName: "Solid"
2021-02-05 15:28:01 +00:00
fontPixelSize: 18
text: FontAwesome . desktop
2021-05-19 16:36:44 +00:00
tooltip: qsTr ( "Grab QR code from screen" ) + translationManager . emptyString
2021-02-05 15:28:01 +00:00
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 ;
2021-06-29 02:34:03 +00:00
} else if ( walletManager . addressValid ( codes [ index ] , appWindow . persistentSettings . nettype ) ) {
fillPaymentDetails ( codes [ index ] ) ;
break ;
2021-02-05 15:28:01 +00:00
}
}
2020-04-18 17:25:45 +00:00
}
2021-02-05 15:28:01 +00:00
}
MoneroComponents . InlineButton {
2021-05-08 11:38:35 +00:00
fontFamily: FontAwesome . fontFamilySolid
fontStyleName: "Solid"
2021-02-05 15:28:01 +00:00
text: FontAwesome . qrcode
visible: appWindow . qrScannerEnabled
2021-05-19 16:36:44 +00:00
tooltip: qsTr ( "Scan QR code" ) + translationManager . emptyString
2021-02-05 15:28:01 +00:00
onClicked: {
cameraUi . state = "Capture"
cameraUi . qrcode_decoded . connect ( updateFromQrCode )
2020-04-18 17:25:45 +00:00
}
2021-02-05 15:28:01 +00:00
}
2020-04-18 17:25:45 +00:00
2021-02-05 15:28:01 +00:00
MoneroComponents . InlineButton {
fontFamily: FontAwesome . fontFamily
text: FontAwesome . addressBook
2021-05-19 16:36:44 +00:00
tooltip: qsTr ( "Import from address book" ) + translationManager . emptyString
2021-02-05 15:28:01 +00:00
onClicked: {
middlePanel . addressBookView . selectAndSend = true ;
appWindow . showPageRequest ( "AddressBook" ) ;
}
}
2017-12-06 13:09:37 +00:00
2021-02-05 15:28:01 +00:00
Item {
Layout.fillWidth: true
}
}
2017-02-27 21:05:28 +00:00
2021-06-04 16:22:45 +00:00
RowLayout {
id: amountLabel
spacing: 6
Layout.preferredWidth: 125
Layout.maximumWidth: recipientLayout . secondRowWidth
MoneroComponents . TextPlain {
font.family: MoneroComponents . Style . fontRegular . name
font.pixelSize: 16
color: MoneroComponents . Style . defaultFontColor
text: qsTr ( "Amount" ) + translationManager . emptyString
}
MoneroComponents . InlineButton {
fontFamily: FontAwesome . fontFamilySolid
fontStyleName: "Solid"
fontPixelSize: 16
text: FontAwesome . infinity
visible: recipientModel . count == 1
tooltip: qsTr ( "Send all unlocked balance of this account" ) + translationManager . emptyString
onClicked: recipientRepeater . itemAt ( 0 ) . children [ 1 ] . children [ 2 ] . text = "(all)" ;
}
Item {
Layout.fillWidth: true
}
2021-02-05 15:28:01 +00:00
}
Item {
Layout.preferredWidth: recipientLayout . thirdRowWidth
}
}
Repeater {
id: recipientRepeater
model: recipientModel
ColumnLayout {
spacing: 0
Rectangle {
Layout.fillWidth: true
2021-06-04 15:33:19 +00:00
Layout.topMargin: - 1
2021-06-19 19:16:54 +00:00
Layout.leftMargin: 1
Layout.rightMargin: recipientLayout . thirdRowWidth + 1
2021-02-05 15:28:01 +00:00
color: MoneroComponents . Style . inputBorderColorInActive
height: 1
visible: index > 0
}
RowLayout {
spacing: 0
MoneroComponents . LineEditMulti {
KeyNavigation.backtab: index > 0 ? recipientRepeater . itemAt ( index - 1 ) . children [ 1 ] . children [ 2 ] : sendButton
KeyNavigation.tab: parent . children [ 2 ]
Layout.alignment: Qt . AlignVCenter
2021-06-19 19:16:54 +00:00
Layout.topMargin: index > 0 ? 0 : 1
Layout.bottomMargin: 2
2021-02-05 15:28:01 +00:00
Layout.fillWidth: true
addressValidation: true
borderDisabled: true
2021-07-25 18:09:52 +00:00
fontColor: error && text != "" ? MoneroComponents.Style.errorColor : MoneroComponents . Style . defaultFontColor
2021-02-05 15:28:01 +00:00
fontFamily: MoneroComponents . Style . fontMonoRegular . name
fontSize: 14
inputPaddingBottom: 0
inputPaddingTop: 0
inputPaddingRight: 0
placeholderFontFamily: MoneroComponents . Style . fontMonoRegular . name
placeholderFontSize: 14
spacing: 0
wrapMode: Text . WrapAnywhere
placeholderText: {
if ( persistentSettings . nettype == NetworkType . MAINNET ) {
2021-09-20 11:31:22 +00:00
return "4.. / 8.. / monero:.. / OpenAlias" ;
2021-02-05 15:28:01 +00:00
} else if ( persistentSettings . nettype == NetworkType . STAGENET ) {
2021-09-20 11:31:22 +00:00
return "5.. / 7.. / monero:.." ;
2021-02-05 15:28:01 +00:00
} else if ( persistentSettings . nettype == NetworkType . TESTNET ) {
2021-09-20 11:31:22 +00:00
return "9.. / B.. / monero:.." ;
2021-02-05 15:28:01 +00:00
}
}
onTextChanged: {
const parsed = walletManager . parse_uri_to_object ( text ) ;
if ( ! parsed . error ) {
2021-07-31 08:42:13 +00:00
fillPaymentDetails ( parsed . address , parsed . payment_id , parsed . amount , parsed . tx_description , parsed . recipient_name ) ;
2021-02-05 15:28:01 +00:00
}
address = text ;
}
text: address
MoneroComponents . InlineButton {
small: true
text: qsTr ( "Resolve" ) + translationManager . emptyString
visible: TxUtils . isValidOpenAliasAddress ( address )
onClicked: {
var result = walletManager . resolveOpenAlias ( address )
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 ? address + " " + descriptionLine.text : address
descriptionCheckbox . checked = true
recipientRepeater . itemAt ( index ) . children [ 1 ] . children [ 0 ] . text = parts [ 1 ] ;
}
else
oa_message ( qsTr ( "No valid address found at this OpenAlias address" ) )
}
else if ( parts [ 0 ] === "false" ) {
if ( address_ok ) {
recipientRepeater . itemAt ( index ) . children [ 1 ] . children [ 0 ] . 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" ) )
}
}
}
}
Rectangle {
Layout.fillHeight: true
2021-06-19 19:16:54 +00:00
Layout.topMargin: index > 0 ? 0 : 1
Layout.bottomMargin: 1
2021-02-05 15:28:01 +00:00
Layout.leftMargin: recipientLayout . colSpacing / 2 - width
Layout.rightMargin: recipientLayout . colSpacing / 2
color: MoneroComponents . Style . inputBorderColorInActive
width: 1
}
MoneroComponents . LineEdit {
KeyNavigation.backtab: parent . children [ 0 ]
KeyNavigation.tab: index + 1 < recipientRepeater . count ? recipientRepeater . itemAt ( index + 1 ) . children [ 1 ] . children [ 0 ] : sendButton
Layout.alignment: Qt . AlignVCenter
2021-06-19 19:16:54 +00:00
Layout.topMargin: index > 0 ? 0 : 1
Layout.bottomMargin: 2
2021-02-05 15:28:01 +00:00
Layout.rightMargin: recipientLayout . colSpacing / 2
Layout.preferredWidth: 125
2021-06-09 03:42:43 +00:00
Layout.maximumWidth: 125
2021-02-05 15:28:01 +00:00
borderDisabled: true
fontFamily: MoneroComponents . Style . fontMonoRegular . name
fontSize: 14
2021-06-09 03:42:43 +00:00
inputPaddingLeft: 0
inputPaddingRight: 0
inputPaddingTop: 0
inputPaddingBottom: 0
2021-02-05 15:28:01 +00:00
placeholderFontFamily: MoneroComponents . Style . fontMonoRegular . name
placeholderFontSize: 14
placeholderLeftMargin: 0
placeholderText: "0.00"
text: amount
onTextChanged: {
text = text . trim ( ) . replace ( "," , "." ) ;
const match = text . match ( /^0+(\d.*)/ ) ;
if ( match ) {
const cursorPosition = cursorPosition ;
text = match [ 1 ] ;
cursorPosition = Math . max ( cursorPosition , 1 ) - 1 ;
} else if ( text . indexOf ( '.' ) === 0 ) {
text = '0' + text ;
if ( text . length > 2 ) {
cursorPosition = 1 ;
}
}
error = walletManager . amountFromString ( text ) > appWindow . getUnlockedBalance ( ) ;
amount = text ;
}
validator: RegExpValidator {
regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/
}
}
MoneroComponents . TextPlain {
Layout.leftMargin: recipientLayout . colSpacing / 2
Layout.preferredWidth: recipientLayout . thirdRowWidth
font.family: FontAwesome . fontFamilySolid
font.styleName: "Solid"
horizontalAlignment: Text . AlignHCenter
opacity: mouseArea . containsMouse ? 1 : 0.85
2021-06-04 16:22:45 +00:00
text: FontAwesome . times
tooltip: qsTr ( "Remove recipient" ) + translationManager . emptyString
2021-05-19 16:36:44 +00:00
tooltipLeft: true
2021-06-04 16:22:45 +00:00
visible: recipientModel . count > 1
2021-02-05 15:28:01 +00:00
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: Qt . PointingHandCursor
hoverEnabled: true
2021-05-19 16:36:44 +00:00
onEntered: parent . tooltipPopup . open ( )
onExited: parent . tooltipPopup . close ( )
2021-06-04 16:22:45 +00:00
onClicked: recipientModel . remove ( index ) ;
2021-02-05 15:28:01 +00:00
}
2019-12-05 10:13:29 +00:00
}
2021-06-04 16:22:45 +00:00
MoneroComponents . TextPlain {
Layout.leftMargin: recipientLayout . colSpacing / 2
Layout.preferredWidth: recipientLayout . thirdRowWidth
horizontalAlignment: Text . AlignHCenter
font.family: MoneroComponents . Style . fontRegular . name
text: "XMR"
visible: recipientModel . count == 1
}
2019-12-05 10:13:29 +00:00
}
2021-02-05 15:28:01 +00:00
}
}
GridLayout {
id: totalLayout
Layout.topMargin: recipientLayout . rowSpacing / 2
Layout.fillWidth: true
columns: 3
columnSpacing: recipientLayout . colSpacing
rowSpacing: 0
RowLayout {
Layout.column: 0
Layout.row: 0
Layout.fillWidth: true
2021-06-19 19:16:54 +00:00
Layout.topMargin: recipientModel . count > 1 ? 0 : - 1
2021-02-05 15:28:01 +00:00
spacing: 0
CheckBox {
border: false
checked: false
enabled: {
if ( recipientModel . count > 0 && recipientModel . get ( 0 ) . amount == "(all)" ) {
return false ;
}
if ( recipientModel . count >= recipientModel . maxRecipients ) {
return false ;
}
return true ;
}
fontAwesomeIcons: true
fontSize: descriptionLine . labelFontSize
iconOnTheLeft: true
text: qsTr ( "Add recipient" ) + translationManager . emptyString
toggleOnClick: false
uncheckedIcon: FontAwesome . plusCircle
onClicked: {
recipientModel . newRecipient ( "" , "" ) ;
}
}
MoneroComponents . TextPlain {
Layout.fillWidth: true
horizontalAlignment: Text . AlignRight
font.family: MoneroComponents . Style . fontRegular . name
font.pixelSize: 16
text: recipientModel . count > 1 ? qsTr ( "Total" ) + translationManager.emptyString : ""
}
}
2020-12-24 08:05:14 +00:00
2021-02-05 15:28:01 +00:00
MoneroComponents . LineEdit {
id: totalValue
Layout.column: 1
Layout.row: 0
Layout.preferredWidth: recipientLayout . secondRowWidth
2021-06-19 19:16:54 +00:00
Layout.topMargin: recipientModel . count > 1 ? 0 : - 1
2021-06-09 03:42:43 +00:00
Layout.maximumWidth: recipientLayout . secondRowWidth
2021-02-05 15:28:01 +00:00
borderDisabled: true
fontFamily: MoneroComponents . Style . fontMonoRegular . name
fontSize: 14
inputHeight: 30
2021-06-09 03:42:43 +00:00
inputPaddingLeft: 0
inputPaddingRight: 0
inputPaddingTop: 0
inputPaddingBottom: 0
2021-02-05 15:28:01 +00:00
readOnly: true
text: Utils . removeTrailingZeros ( walletManager . displayAmount ( recipientModel . getAmountTotal ( ) ) )
visible: recipientModel . count > 1
}
MoneroComponents . TextPlain {
Layout.column: 2
Layout.row: 0
Layout.preferredWidth: recipientLayout . thirdRowWidth
2021-06-09 03:42:43 +00:00
Layout.maximumWidth: recipientLayout . thirdRowWidth
2021-02-05 15:28:01 +00:00
horizontalAlignment: Text . AlignHCenter
font.family: MoneroComponents . Style . fontRegular . name
text: "XMR"
visible: recipientModel . count > 1
}
MoneroComponents . LineEdit {
Layout.column: 1
Layout.row: recipientModel . count > 1 ? 1 : 0
Layout.preferredWidth: recipientLayout . secondRowWidth
2021-06-19 19:16:54 +00:00
Layout.topMargin: recipientModel . count > 1 ? 0 : - 1
2021-06-09 03:42:43 +00:00
Layout.maximumWidth: recipientLayout . secondRowWidth
2021-02-05 15:28:01 +00:00
borderDisabled: true
fontFamily: MoneroComponents . Style . fontMonoRegular . name
fontSize: 14
inputHeight: 30
2021-06-09 03:42:43 +00:00
inputPaddingLeft: 0
inputPaddingRight: 0
inputPaddingTop: 0
inputPaddingBottom: 0
2021-02-05 15:28:01 +00:00
opacity: 0.7
readOnly: true
text: fiatApiConvertToFiat ( walletManager . displayAmount ( recipientModel . getAmountTotal ( ) ) )
visible: persistentSettings . fiatPriceEnabled
2020-12-24 08:05:14 +00:00
}
2021-02-05 15:28:01 +00:00
MoneroComponents . TextPlain {
Layout.column: 2
Layout.row: recipientModel . count > 1 ? 1 : 0
Layout.preferredWidth: recipientLayout . thirdRowWidth
2021-06-19 19:16:54 +00:00
Layout.topMargin: recipientModel . count > 1 ? 0 : - 1
2021-06-09 03:42:43 +00:00
Layout.maximumWidth: recipientLayout . thirdRowWidth
2021-02-05 15:28:01 +00:00
font.family: MoneroComponents . Style . fontRegular . name
horizontalAlignment: Text . AlignHCenter
opacity: 0.7
text: fiatApiCurrencySymbol ( )
visible: persistentSettings . fiatPriceEnabled
}
}
}
Rectangle {
anchors.top: recipientLayout . top
anchors.topMargin: addressLabel . height + recipientLayout . rowSpacing / 2
anchors.bottom: recipientLayout . bottom
anchors.bottomMargin: totalLayout . height + recipientLayout . rowSpacing / 2
anchors.left: recipientLayout . left
anchors.right: recipientLayout . right
anchors.rightMargin: recipientLayout . thirdRowWidth
color: "transparent"
border.color: MoneroComponents . Style . inputBorderColorInActive
border.width: 1
radius: 4
}
}
ColumnLayout {
spacing: 0
visible: appWindow . walletMode >= 2
// 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.
// Priorites after v5
ListModel {
id: priorityModelV5
ListElement { column1: qsTr ( "Automatic" ) ; column2: "" ; priority: 0 }
ListElement { column1: qsTr ( "Slow (x0.2 fee)" ) ; column2: "" ; priority: 1 }
ListElement { column1: qsTr ( "Normal (x1 fee)" ) ; column2: "" ; priority: 2 }
ListElement { column1: qsTr ( "Fast (x5 fee)" ) ; column2: "" ; priority: 3 }
ListElement { column1: qsTr ( "Fastest (x200 fee)" ) ; column2: "" ; priority: 4 }
}
RowLayout {
Layout.topMargin: 5
spacing: 10
StandardDropdown {
2021-06-19 14:42:15 +00:00
Layout.maximumWidth: 200
2021-02-05 15:28:01 +00:00
id: priorityDropdown
currentIndex: 0
dataModel: priorityModelV5
2021-06-19 14:42:15 +00:00
labelText: qsTr ( "Transaction priority" ) + translationManager . emptyString
2021-06-22 10:37:48 +00:00
labelFontSize: 16
2021-02-05 15:28:01 +00:00
}
2020-01-22 16:32:19 +00:00
MoneroComponents . TextPlain {
id: feeLabel
2021-06-30 23:19:57 +00:00
Layout.alignment: Qt . AlignBottom
Layout.bottomMargin: 11
2020-01-22 16:32:19 +00:00
font.family: MoneroComponents . Style . fontRegular . name
font.pixelSize: 14
color: MoneroComponents . Style . defaultFontColor
2021-02-05 15:28:01 +00:00
opacity: 0.7
2020-01-22 16:32:19 +00:00
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 ;
}
2021-02-05 15:28:01 +00:00
var addresses = [ ] ;
var amounts = [ ] ;
for ( var index = 0 ; index < recipientModel . count ; ++ index ) {
const recipient = recipientModel . get ( index ) ;
addresses . push ( recipient . address ) ;
amounts . push ( walletManager . amountFromString ( recipient . amount ) ) ;
}
2020-01-22 16:32:19 +00:00
currentWallet . estimateTransactionFeeAsync (
2021-02-05 15:28:01 +00:00
addresses ,
amounts ,
2020-01-22 16:32:19 +00:00
priorityModelV5 . get ( priorityDropdown . currentIndex ) . priority ,
function ( amount ) {
2021-02-05 15:28:01 +00:00
if ( amount ) {
estimatedFee = Utils . removeTrailingZeros ( amount ) ;
}
2020-01-22 16:32:19 +00:00
estimating = false ;
} ) ;
}
text: {
if ( ! sendButton . enabled || estimatedFee == null ) {
return ""
}
2021-02-05 15:28:01 +00:00
return "~%1 XMR%2 %3" . arg ( estimatedFee )
. arg ( estimatedFeeFiat )
. arg ( qsTr ( "fee" ) + translationManager . emptyString ) ;
2020-01-22 16:32:19 +00:00
}
BusyIndicator {
2021-02-05 15:28:01 +00:00
anchors.left: parent . left
2020-01-22 16:32:19 +00:00
running: feeLabel . estimating
height: parent . height
2021-02-05 15:28:01 +00:00
width: height
2020-01-22 16:32:19 +00:00
}
}
2021-02-05 15:28:01 +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
}
}
2021-06-26 19:25:19 +00:00
LineEdit {
2019-04-17 05:30:17 +00:00
id: descriptionLine
2021-06-26 19:25:19 +00:00
placeholderFontSize: 16
fontSize: 16
placeholderText: qsTr ( "Saved to local wallet history" ) + " (" + qsTr ( "only visible to you" ) + ")" + translationManager . emptyString
2019-04-17 05:30:17 +00:00
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
2019-04-11 01:17:29 +00:00
rightIcon: "qrc:///images/rightArrow.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
2021-02-05 15:28:01 +00:00
enabled: ! sendButtonWarningBox . visible && ! warningContent && ! recipientModel . hasEmptyAddress ( ) && ! 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 )
2018-12-21 16:32:26 +00:00
setPaymentId ( paymentIdLine . text . trim ( ) ) ;
2021-02-05 15:28:01 +00:00
root . paymentClicked ( recipientModel . getRecipients ( ) , paymentIdLine . 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
2021-02-05 15:28:01 +00:00
function checkInformation ( ) {
return ! recipientModel . hasEmptyAmount ( ) &&
recipientModel . getAmountTotal ( ) <= appWindow . getUnlockedBalance ( ) &&
! recipientModel . hasInvalidAddress ( ) ;
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
}
}
2021-04-24 04:10:52 +00:00
AdvancedOptionsItem {
visible: persistentSettings . transferShowAdvanced && appWindow . walletMode >= 2
title: qsTr ( "Outputs" ) + translationManager . emptyString
button1.text: qsTr ( "Export" ) + translationManager . emptyString
button1.enabled: appWindow . viewOnly
button1.onClicked: {
console . log ( "Transfer: export outputs clicked" )
exportOutputsDialog . open ( ) ;
}
button2.text: qsTr ( "Import" ) + translationManager . emptyString
button2.enabled: ! appWindow . viewOnly
button2.onClicked: {
console . log ( "Transfer: import outputs clicked" )
importOutputsDialog . open ( ) ;
}
tooltip: {
var header = qsTr ( "Required for cold wallets to sign their corresponding key images" ) + translationManager . emptyString ;
return "<style type='text/css'>.header{ font-size: 13px; } p{line-height:20px; margin-top:0px; margin-bottom:0px; " +
";} p.orange{color:#ff9323;}</style>" +
"<div class='header'>" + header + "</div>" +
"<p>" + qsTr ( "1. Using view-only wallet, export the outputs into a file" ) + "</p>" +
"<p>" + qsTr ( "2. Using cold wallet, import the outputs file" ) + "</p>" +
translationManager . emptyString
}
}
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
}
2021-04-15 00:04:50 +00:00
tooltip: {
2020-04-16 21:55:22 +00:00
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
}
2021-04-15 00:04:50 +00:00
var header = qsTr ( "Required for view-only wallets to display the real balance" ) + translationManager . emptyString ;
return "<style type='text/css'>.header{ font-size: 13px; } p{line-height:20px; margin-top:0px; margin-bottom:0px; " +
2020-10-07 10:59:33 +00:00
";} p.orange{color:#ff9323;}</style>" +
2021-04-15 00:04:50 +00:00
"<div class='header'>" + header + "</div>" +
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
}
2021-04-15 00:04:50 +00:00
2020-04-16 21:55:22 +00:00
AdvancedOptionsItem {
visible: persistentSettings . transferShowAdvanced && appWindow . walletMode >= 2
title: qsTr ( "Offline transaction signing" ) + translationManager . emptyString
button1.text: qsTr ( "Create" ) + translationManager . emptyString
2021-02-05 15:28:01 +00:00
button1.enabled: appWindow . viewOnly && pageRoot . checkInformation ( )
2020-04-16 21:55:22 +00:00
button1.onClicked: {
console . log ( "Transfer: saveTx Clicked" )
var priority = priorityModelV5 . get ( priorityDropdown . currentIndex ) . priority
console . log ( "priority: " + priority )
setPaymentId ( paymentIdLine . text . trim ( ) ) ;
2021-02-05 15:28:01 +00:00
root . paymentClicked ( recipientModel . getRecipients ( ) , paymentIdLine . 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 ( ) ;
}
2021-04-15 00:04:50 +00:00
tooltip: {
2020-04-16 21:55:22 +00:00
var errorMessage = "" ;
2021-02-05 15:28:01 +00:00
if ( appWindow . viewOnly && ! pageRoot . checkInformation ( ) ) {
2020-04-16 21:55:22 +00:00
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
}
2021-04-15 00:04:50 +00:00
var header = qsTr ( "Spend XMR from a cold (offline) wallet" ) + translationManager . emptyString ;
return "<style type='text/css'>.header{ font-size: 13px; } p{line-height:20px; margin-top:0px; margin-bottom:0px; " +
2020-10-07 10:59:33 +00:00
";} p.orange{color:#ff9323;}</style>" +
2021-04-15 00:04:50 +00:00
"<div class='header'>" + header + "</div>" +
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
}
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
}
2021-04-15 00:04:50 +00:00
tooltip: 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
2021-04-24 04:10:52 +00:00
FileDialog {
id: exportOutputsDialog
selectMultiple: false
selectExisting: false
onAccepted: {
console . log ( walletManager . urlToLocalPath ( exportOutputsDialog . fileUrl ) )
if ( currentWallet . exportOutputs ( walletManager . urlToLocalPath ( exportOutputsDialog . fileUrl ) , true ) ) {
appWindow . showStatusMessage ( qsTr ( "Outputs successfully exported to file" ) + translationManager . emptyString , 3 ) ;
} else {
appWindow . showStatusMessage ( currentWallet . errorString , 5 ) ;
}
}
onRejected: {
console . log ( "Canceled" ) ;
}
}
FileDialog {
id: importOutputsDialog
selectMultiple: false
selectExisting: true
title: qsTr ( "Please choose a file" ) + translationManager . emptyString
onAccepted: {
console . log ( walletManager . urlToLocalPath ( importOutputsDialog . fileUrl ) )
if ( currentWallet . importOutputs ( walletManager . urlToLocalPath ( importOutputsDialog . fileUrl ) ) ) {
appWindow . showStatusMessage ( qsTr ( "Outputs successfully imported to wallet" ) + translationManager . emptyString , 3 ) ;
} else {
appWindow . showStatusMessage ( currentWallet . errorString , 5 ) ;
}
}
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 ) )
2021-09-03 20:41:17 +00:00
if ( currentWallet . exportKeyImages ( walletManager . urlToLocalPath ( exportKeyImagesDialog . fileUrl ) , true ) ) {
appWindow . showStatusMessage ( qsTr ( "Key images successfully exported to file" ) + translationManager . emptyString , 3 ) ;
} else {
appWindow . showStatusMessage ( currentWallet . errorString , 5 ) ;
}
2018-05-14 21:43:52 +00:00
}
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 ) )
2021-09-03 20:41:17 +00:00
if ( currentWallet . importKeyImages ( walletManager . urlToLocalPath ( importKeyImagesDialog . fileUrl ) ) ) {
appWindow . showStatusMessage ( qsTr ( "Key images successfully imported to wallet" ) + translationManager . emptyString , 3 ) ;
} else {
appWindow . showStatusMessage ( currentWallet . errorString , 5 ) ;
}
2018-05-14 21:43:52 +00:00
}
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.
2021-02-05 15:28:01 +00:00
function sendTo ( address , paymentId , description , amount ) {
2019-03-22 20:02:08 +00:00
middlePanel . state = 'Transfer' ;
2021-02-05 15:28:01 +00:00
fillPaymentDetails ( address , paymentId , amount , description ) ;
2016-12-10 01:01:04 +00:00
}
2014-07-07 17:08:30 +00:00
}