2019-04-11 01:17:29 +00:00
import QtQuick 2.9
2018-12-08 15:55:29 +00:00
import QtQuick . Layouts 1.1
import QtQuick . Controls 2.0
import QtGraphicalEffects 1.0
import QtQuick . Controls . Styles 1.4
import QtQuick . Dialogs 1.2
import moneroComponents . Clipboard 1.0
import moneroComponents . Wallet 1.0
import moneroComponents . WalletManager 1.0
import moneroComponents . TransactionHistory 1.0
import moneroComponents . TransactionHistoryModel 1.0
import moneroComponents . Subaddress 1.0
import moneroComponents . SubaddressModel 1.0
import "../../js/Windows.js" as Windows
import "../../js/TxUtils.js" as TxUtils
import "../../js/Utils.js" as Utils
import "../../components" as MoneroComponents
import "../../pages"
import "."
Item {
id: root
anchors.margins: 0
2019-04-25 19:09:23 +00:00
property int minWidth: 900
2020-05-10 22:42:45 +00:00
property int minHeight: 600
2019-04-25 19:09:23 +00:00
property int qrCodeSize: 220
2018-12-08 15:55:29 +00:00
property bool enableTracking: false
property string trackingError: "" // setting this will show a message @ tracking table
property alias merchantHeight: mainLayout . height
property string addressLabel: ""
property var hiddenAmounts: [ ]
function onPageCompleted ( ) {
2020-04-22 20:00:44 +00:00
if ( appWindow . currentWallet ) {
appWindow . current_address = appWindow . currentWallet . address ( appWindow . currentWallet . currentSubaddressAccount , 0 )
}
2018-12-08 15:55:29 +00:00
// prepare tracking
trackingCheckbox . checked = root . enableTracking
root . update ( ) ;
timer . running = true ;
// set currently selected account indication
var _addressLabel = appWindow . currentWallet . getSubaddressLabel (
appWindow . currentWallet . currentSubaddressAccount ,
appWindow . current_subaddress_table_index ) ;
if ( _addressLabel === "" ) {
root . addressLabel = "#" + appWindow . current_subaddress_table_index ;
} else {
root . addressLabel = _addressLabel ;
}
}
function onPageClosed ( ) {
// reset component objects
timer . running = false
root . enableTracking = false
trackingModel . clear ( )
}
Image {
anchors.top: parent . top
anchors.left: parent . left
anchors.right: parent . right
2019-04-25 19:09:23 +00:00
height: 300
2019-04-11 01:17:29 +00:00
source: "qrc:///images/merchant/bg.png"
2018-12-08 15:55:29 +00:00
smooth: false
}
ColumnLayout {
id: mainLayout
2020-05-10 22:42:45 +00:00
visible: parent . width >= root . minWidth && appWindow . height >= root . minHeight
2018-12-08 15:55:29 +00:00
spacing: 0
// emulates max-width + center for container
2019-04-25 19:09:23 +00:00
property int maxWidth: 1200
property int defaultMargin: 50
2018-12-08 15:55:29 +00:00
property int horizontalMargin: {
if ( appWindow . width >= maxWidth ) {
return ( ( appWindow . width - maxWidth ) / 2 ) + defaultMargin ;
} else {
return defaultMargin ;
}
}
anchors.leftMargin: horizontalMargin
anchors.rightMargin: horizontalMargin
anchors.margins: defaultMargin
anchors.left: parent . left
anchors.top: parent . top
anchors.right: parent . right
Item {
2019-07-21 19:51:47 +00:00
Layout.preferredHeight: 220
Layout.fillWidth: true
2018-12-08 15:55:29 +00:00
Rectangle {
id: tracker
anchors.left: parent . left
anchors.top: parent . top
2019-04-25 19:09:23 +00:00
height: 220
width: ( parent . width - qrImg . width ) - 50
2018-12-08 15:55:29 +00:00
radius: 5
ColumnLayout {
anchors.top: parent . top
anchors.left: parent . left
anchors.right: parent . right
spacing: 0
RowLayout {
spacing: 0
2019-04-25 19:09:23 +00:00
height: 56
2018-12-08 15:55:29 +00:00
RowLayout {
Layout.alignment: Qt . AlignLeft
2019-04-25 19:09:23 +00:00
Layout.preferredWidth: 260
2018-12-08 15:55:29 +00:00
Layout.preferredHeight: parent . height
Layout.fillHeight: true
2019-04-25 19:09:23 +00:00
spacing: 8
2018-12-08 15:55:29 +00:00
Item {
2019-04-25 19:09:23 +00:00
Layout.preferredWidth: 10
2018-12-08 15:55:29 +00:00
}
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2019-04-25 19:09:23 +00:00
font.pixelSize: 16
2018-12-08 15:55:29 +00:00
font.bold: true
color: "#767676"
2019-04-19 11:35:15 +00:00
text: qsTr ( "Sales" ) + translationManager . emptyString
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
}
Item {
Layout.fillWidth: true
}
}
Item {
Layout.fillWidth: true
}
}
Rectangle {
Layout.fillWidth: true
2019-04-25 19:09:23 +00:00
Layout.preferredHeight: 1
2018-12-08 15:55:29 +00:00
color: "#d9d9d9"
}
MerchantTrackingList {
Layout.fillWidth: true
2019-04-25 19:09:23 +00:00
Layout.preferredHeight: 400
2018-12-08 15:55:29 +00:00
model: trackingModel
message: {
if ( ! root . enableTracking ) {
2019-12-08 21:06:31 +00:00
return "<style>p{font-size:14px;}</style> <p>%1</p> <p>%2</p>"
. arg ( qsTr ( "This page will automatically scan the blockchain and the tx pool for incoming transactions using the QR code." ) )
. arg ( qsTr ( "It's up to you whether to accept unconfirmed transactions or not. It is likely they'll be confirmed in short order, but there is still a possibility they might not, so for larger values you may want to wait for one or more confirmation(s)" ) )
+ translationManager . emptyString ;
2018-12-08 15:55:29 +00:00
} else if ( root . trackingError !== "" ) {
return root . trackingError ;
} else if ( trackingModel . count < 1 ) {
return qsTr ( "Currently monitoring incoming transactions, none found yet." ) ;
} else {
return ""
}
}
onHideAmountToggled: {
if ( root . hiddenAmounts . indexOf ( txid ) < 0 ) {
root . hiddenAmounts . push ( txid ) ;
} else {
root . hiddenAmounts = root . hiddenAmounts . filter ( function ( _txid ) { return _txid !== txid } ) ;
}
}
}
}
}
DropShadow {
anchors.fill: source
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: "#20000000"
smooth: true
source: tracker
}
Rectangle {
id: qrImg
color: "white"
anchors.right: parent . right
anchors.top: parent . top
height: root . qrCodeSize
width: root . qrCodeSize
Layout.maximumWidth: root . qrCodeSize
Layout.preferredHeight: width
radius: 5
Image {
id: qrCode
anchors.fill: parent
2019-04-25 19:09:23 +00:00
anchors.margins: 1
2018-12-08 15:55:29 +00:00
smooth: false
fillMode: Image . PreserveAspectFit
source: "image://qrcode/" + TxUtils . makeQRCodeString ( appWindow . current_address , amountToReceive . text )
MouseArea {
anchors.fill: parent
acceptedButtons: Qt . RightButton
onClicked: {
if ( mouse . button == Qt . RightButton ) {
qrMenu . x = this . mouseX ;
qrMenu . y = this . mouseY ;
qrMenu . open ( )
}
}
onPressAndHold: qrFileDialog . open ( )
}
}
Menu {
id: qrMenu
title: "QrCode"
MenuItem {
text: qsTr ( "Save As" ) + translationManager . emptyString ;
onTriggered: qrFileDialog . open ( )
}
}
}
DropShadow {
anchors.fill: source
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: "#30000000"
smooth: true
source: qrImg
}
}
Item {
2019-04-25 19:09:23 +00:00
Layout.preferredHeight: 40
2019-07-21 19:51:47 +00:00
Layout.fillWidth: true
2018-12-08 15:55:29 +00:00
Item {
2019-04-25 19:09:23 +00:00
width: ( parent . width - qrImg . width ) - ( 50 )
height: 32
2018-12-08 15:55:29 +00:00
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2018-12-08 15:55:29 +00:00
anchors.verticalCenter: parent . verticalCenter
anchors.horizontalCenter: parent . horizontalCenter
2019-04-25 19:09:23 +00:00
font.pixelSize: 12
2018-12-08 15:55:29 +00:00
font.bold: false
color: "white"
2020-01-28 16:31:04 +00:00
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 12px;}</style>%1: %2 <a href='#'>(%3)</a>"
. arg ( qsTr ( "Currently selected address" ) )
. arg ( addressLabel )
. arg ( qsTr ( "Change" ) ) + translationManager . emptyString
2018-12-08 15:55:29 +00:00
textFormat: Text . RichText
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt . PointingHandCursor
onClicked: appWindow . showPageRequest ( "Receive" )
}
}
}
Item {
anchors.right: parent . right
anchors.top: parent . top
2019-04-25 19:09:23 +00:00
width: 220
height: 32
2018-12-08 15:55:29 +00:00
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2018-12-08 15:55:29 +00:00
anchors.verticalCenter: parent . verticalCenter
anchors.horizontalCenter: parent . horizontalCenter
2019-04-25 19:09:23 +00:00
font.pixelSize: 12
2018-12-08 15:55:29 +00:00
font.bold: false
color: "white"
2019-04-19 11:35:15 +00:00
text: qsTr ( "(right-click, save as)" ) + translationManager . emptyString
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
}
}
}
Item {
2019-04-25 19:09:23 +00:00
Layout.preferredHeight: 120
Layout.topMargin: 20
2018-12-08 15:55:29 +00:00
Layout.fillWidth: true
Rectangle {
id: payment_url_container
anchors.left: parent . left
anchors.top: parent . top
2019-04-25 19:09:23 +00:00
implicitHeight: 120
width: ( parent . width - qrImg . width ) - ( 50 )
2018-12-08 15:55:29 +00:00
radius: 5
ColumnLayout {
anchors.top: parent . top
anchors.left: parent . left
anchors.right: parent . right
spacing: 0
RowLayout {
spacing: 0
2019-04-25 19:09:23 +00:00
height: 56
2018-12-08 15:55:29 +00:00
RowLayout {
Layout.alignment: Qt . AlignLeft
2019-04-25 19:09:23 +00:00
Layout.preferredWidth: 260
2018-12-08 15:55:29 +00:00
Layout.preferredHeight: parent . height
Layout.fillHeight: true
spacing: 8
Item {
2019-04-25 19:09:23 +00:00
Layout.preferredWidth: 10
2018-12-08 15:55:29 +00:00
}
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2019-04-25 19:09:23 +00:00
font.pixelSize: 14
2018-12-08 15:55:29 +00:00
font.bold: true
color: "#767676"
2019-04-19 11:35:15 +00:00
text: qsTr ( "Payment URL" ) + translationManager . emptyString
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
}
Item {
Layout.fillWidth: true
}
}
// @TODO: PaymentURL explanation
// Rectangle {
// // help box
// Layout.alignment: Qt.AlignLeft
2019-04-25 19:09:23 +00:00
// Layout.preferredWidth: 40
2018-12-08 15:55:29 +00:00
// Layout.fillHeight: true
// color: "transparent"
2019-04-11 01:17:29 +00:00
// MoneroComponents.TextPlain {
2018-12-08 15:55:29 +00:00
// anchors.verticalCenter: parent.verticalCenter
// anchors.right: parent.right
2019-04-25 19:09:23 +00:00
// anchors.rightMargin: 20
// font.pixelSize: 16
2018-12-08 15:55:29 +00:00
// font.bold: true
// color: "#767676"
// text:"?"
// }
// MouseArea {
// anchors.fill: parent
// cursorShape: Qt.PointingHandCursor
// onClicked: {
// merchantPageDialog.title = qsTr("Payment URL") + translationManager.emptyString;
// merchantPageDialog.text = qsTr("payment url explanation")
// merchantPageDialog.icon = StandardIcon.Information
// merchantPageDialog.open()
// }
// }
// }
Item {
Layout.fillWidth: true
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#d9d9d9"
}
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2018-12-08 15:55:29 +00:00
property string _color : "#767676"
Layout.fillWidth: true
2019-04-25 19:09:23 +00:00
Layout.margins: 20
Layout.topMargin: 10
2018-12-08 15:55:29 +00:00
wrapMode: Text . WrapAnywhere
elide: Text . ElideRight
2019-04-25 19:09:23 +00:00
font.pixelSize: 12
2018-12-08 15:55:29 +00:00
font.bold: true
color: _color
text: TxUtils . makeQRCodeString ( appWindow . current_address , amountToReceive . text )
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt . PointingHandCursor
onEntered: {
parent . color = MoneroComponents . Style . orange
}
onExited: {
parent . color = parent . _color
}
onClicked: {
console . log ( "Copied to clipboard" ) ;
clipboard . setText ( parent . text ) ;
appWindow . showStatusMessage ( qsTr ( "Copied to clipboard" ) , 3 ) ;
}
}
}
}
}
DropShadow {
anchors.fill: source
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: "#20000000"
smooth: true
source: payment_url_container
}
Item {
anchors.right: parent . right
anchors.top: parent . top
2019-04-25 19:09:23 +00:00
width: 220
height: 32
2018-12-08 15:55:29 +00:00
ColumnLayout {
anchors.left: parent . left
anchors.right: parent . right
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2019-04-25 19:09:23 +00:00
font.pixelSize: 14
2018-12-08 15:55:29 +00:00
font.bold: false
color: "white"
2019-12-01 23:08:43 +00:00
text: qsTr ( "Amount to receive" ) + " (XMR)" + translationManager . emptyString
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
}
Image {
2019-04-25 19:09:23 +00:00
height: 28
width: 220
2019-04-11 01:17:29 +00:00
source: "qrc:///images/merchant/input_box.png"
2018-12-08 15:55:29 +00:00
2019-11-08 00:56:10 +00:00
MoneroComponents . Input {
2018-12-08 15:55:29 +00:00
id: amountToReceive
topPadding: 0
2019-04-25 19:09:23 +00:00
leftPadding: 10
2018-12-08 15:55:29 +00:00
anchors.left: parent . left
anchors.right: parent . right
anchors.top: parent . top
2019-04-25 19:09:23 +00:00
anchors.topMargin: 3
font.pixelSize: 16
2018-12-08 15:55:29 +00:00
font.bold: true
horizontalAlignment: TextInput . AlignLeft
verticalAlignment: TextInput . AlignVCenter
selectByMouse: true
color: "#424242"
selectionColor: "#3f3fe3"
selectedTextColor: "white"
2019-02-25 20:17:54 +00:00
placeholderText: "0.00"
2018-12-08 15:55:29 +00:00
background: Rectangle {
color: "transparent"
}
onTextChanged: {
if ( amountToReceive . text . indexOf ( '.' ) === 0 ) {
amountToReceive . text = '0' + amountToReceive . text ;
}
}
validator: RegExpValidator {
regExp: /^(\d{1,8})?([\.]\d{1,12})?$/
}
}
}
Item {
2019-04-25 19:09:23 +00:00
height: 2
width: 220
2018-12-08 15:55:29 +00:00
}
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2018-12-08 15:55:29 +00:00
// @TODO: When we have XMR/USD rate avi. in the future.
visible: false
2019-04-25 19:09:23 +00:00
font.pixelSize: 14
2018-12-08 15:55:29 +00:00
font.bold: false
color: "white"
text: qsTr ( "Amount to receive" ) + " (USD)"
opacity: 0.2
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
}
Image {
visible: false
2019-04-25 19:09:23 +00:00
height: 28
width: 220
2019-04-11 01:17:29 +00:00
source: "qrc:///images/merchant/input_box.png"
2018-12-08 15:55:29 +00:00
opacity: 0.2
}
}
}
}
Item {
2019-04-25 19:09:23 +00:00
Layout.topMargin: 32
Layout.preferredHeight: 40
2019-07-21 19:51:47 +00:00
Layout.fillWidth: true
2018-12-08 15:55:29 +00:00
ColumnLayout {
2019-04-25 19:09:23 +00:00
spacing: 16
2018-12-08 15:55:29 +00:00
MerchantCheckbox {
id: trackingCheckbox
checked: root . enableTracking
2019-04-19 11:35:15 +00:00
text: qsTr ( "Enable sales tracker" ) + translationManager . emptyString
2018-12-08 15:55:29 +00:00
onChanged: {
root . enableTracking = this . checked ;
}
}
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2018-12-08 15:55:29 +00:00
id: content
2019-04-25 19:09:23 +00:00
font.pixelSize: 14
2018-12-08 15:55:29 +00:00
font.bold: false
color: "white"
2019-04-19 11:35:15 +00:00
text: qsTr ( "Leave this page" ) + translationManager . emptyString
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt . PointingHandCursor
2020-04-21 17:42:05 +00:00
onClicked: appWindow . showPageRequest ( "Settings" )
2018-12-08 15:55:29 +00:00
}
}
}
}
}
Rectangle {
// Shows when the window is too small
2020-05-10 22:42:45 +00:00
visible: parent . width < root . minWidth || appWindow . height < root . minHeight
2018-12-08 15:55:29 +00:00
anchors.top: parent . top
2019-04-25 19:09:23 +00:00
anchors.topMargin: 100 ;
2018-12-08 15:55:29 +00:00
anchors.horizontalCenter: parent . horizontalCenter
2019-04-25 19:09:23 +00:00
height: 120
width: 400
2018-12-08 15:55:29 +00:00
radius: 5
2019-04-11 01:17:29 +00:00
MoneroComponents . TextPlain {
2018-12-08 15:55:29 +00:00
anchors.horizontalCenter: parent . horizontalCenter
anchors.verticalCenter: parent . verticalCenter
2019-04-25 19:09:23 +00:00
font.pixelSize: 14
2018-12-08 15:55:29 +00:00
font.bold: true
color: MoneroComponents . Style . moneroGrey
2019-04-19 11:35:15 +00:00
text: qsTr ( "The merchant page requires a larger window" ) + translationManager . emptyString
2019-04-11 01:17:29 +00:00
themeTransition: false
2018-12-08 15:55:29 +00:00
}
2020-05-12 18:43:46 +00:00
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt . PointingHandCursor
onClicked: appWindow . showPageRequest ( "Settings" )
}
2018-12-08 15:55:29 +00:00
}
function update ( ) {
const max_tracking = 3 ;
if ( ! appWindow . currentWallet || ! root . enableTracking ) {
root . trackingError = "" ;
trackingModel . clear ( ) ;
return
}
2020-01-28 04:43:31 +00:00
if ( appWindow . disconnected ) {
2018-12-08 15:55:29 +00:00
root . trackingError = qsTr ( "WARNING: no connection to daemon" ) ;
trackingModel . clear ( ) ;
return
}
var model = appWindow . currentWallet . historyModel
var count = model . rowCount ( )
var nTransactions = 0
var txs = [ ]
// Currently selected subaddress as per Receive page
var current_subaddress_table_index = appWindow . current_subaddress_table_index ;
for ( var i = 0 ; i < count && txs . length < max_tracking ; ++ i ) {
var idx = model . index ( i , 0 )
var isout = model . data ( idx , TransactionHistoryModel . TransactionIsOutRole ) ;
var timeDate = model . data ( idx , TransactionHistoryModel . TransactionDateRole ) ;
var timeHour = model . data ( idx , TransactionHistoryModel . TransactionTimeRole ) ;
2019-01-10 09:39:22 +00:00
var timeEpoch = new Date ( timeDate + "T" + timeHour ) . getTime ( ) / 1000 ;
2018-12-08 15:55:29 +00:00
var subaddrAccount = model . data ( idx , TransactionHistoryModel . TransactionSubaddrAccountRole ) ;
var subaddrIndex = model . data ( idx , TransactionHistoryModel . TransactionSubaddrIndexRole ) ;
if ( ! isout && subaddrAccount == appWindow . currentWallet . currentSubaddressAccount && subaddrIndex == current_subaddress_table_index ) {
nTransactions += 1
var txid = model . data ( idx , TransactionHistoryModel . TransactionHashRole ) ;
var blockHeight = model . data ( idx , TransactionHistoryModel . TransactionBlockHeightRole ) ;
var in_txpool = false ;
var confirmations = 0 ;
2020-04-22 20:05:20 +00:00
var displayAmount = model . data ( idx , TransactionHistoryModel . TransactionDisplayAmountRole ) ;
2018-12-08 15:55:29 +00:00
2020-04-22 20:05:20 +00:00
if ( blockHeight === undefined ) {
2018-12-08 15:55:29 +00:00
in_txpool = true ;
} else {
2020-04-22 20:05:20 +00:00
confirmations = model . data ( idx , TransactionHistoryModel . TransactionConfirmationsRole ) ;
2018-12-08 15:55:29 +00:00
}
txs . push ( {
"amount" : displayAmount ,
"confirmations" : confirmations ,
"in_txpool" : in_txpool ,
"txid" : txid ,
"time_epoch" : timeEpoch ,
"time_date" : timeDate + " " + timeHour ,
"hide_amount" : root . hiddenAmounts . indexOf ( txid ) >= 0
} )
}
}
// Update tracking status label
if ( nTransactions == 0 ) {
root . trackingError = qsTr ( "Currently monitoring incoming transactions, none found yet." ) + translationManager . emptyString
return
}
trackingModel . clear ( ) ;
txs . forEach ( function ( tx ) {
trackingModel . append ( {
"amount" : tx . amount ,
"confirmations" : tx . confirmations ,
"in_txpool" : tx . in_txpool ,
"txid" : tx . txid ,
"time_epoch" : tx . time_epoch ,
"time_date" : tx . time_date ,
"hide_amount" : tx . hide_amount
} ) ;
} ) ;
}
ListModel {
id: trackingModel
}
Timer {
id: timer
interval: 3000 ; running: false ; repeat: true
onTriggered: update ( )
}
MessageDialog {
id: merchantPageDialog
standardButtons: StandardButton . Ok
}
FileDialog {
id: qrFileDialog
title: "Please choose a name"
folder: shortcuts . pictures
selectExisting: false
nameFilters: [ "Image (*.png)" ]
onAccepted: {
if ( ! walletManager . saveQrCode ( TxUtils . makeQRCodeString ( appWindow . current_address , amountToReceive . text ) , walletManager . urlToLocalPath ( fileUrl ) ) ) {
console . log ( "Failed to save QrCode to file " + walletManager . urlToLocalPath ( fileUrl ) )
receivePageDialog . title = qsTr ( "Save QrCode" ) + translationManager . emptyString ;
receivePageDialog . text = qsTr ( "Failed to save QrCode to " ) + walletManager . urlToLocalPath ( fileUrl ) + translationManager . emptyString ;
receivePageDialog . icon = StandardIcon . Error
receivePageDialog . open ( )
}
}
}
Clipboard { id: clipboard }
}