6dc315cc77
This fixes the issue that the "sweep from browser" would consume the property before the QR scanner had time to decide it should skip scanning due to that property existing.
373 lines
13 KiB
QML
373 lines
13 KiB
QML
/*
|
|
* This file is part of the Flowee project
|
|
* Copyright (C) 2024 Tom Zander <tom@flowee.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
import QtQuick
|
|
import QtQuick.Controls as QQC2
|
|
import QtQuick.Layouts
|
|
import "../Flowee" as Flowee
|
|
import "../mobile" as Mobile;
|
|
import Flowee.org.pay;
|
|
import Flowee.org.pay.SendSweep;
|
|
|
|
Mobile.Page {
|
|
id: root
|
|
headerText: qsTr("Sweep coins")
|
|
|
|
Item { // data
|
|
QRScanner {
|
|
id: scanner
|
|
scanType: QRScanner.PrivateKeyWIF
|
|
autostart: Intent.sweepKey === ""
|
|
helpText: qsTr("Scan QR (WIF) to find funds", "Please note that WIF and QR are names")
|
|
onFinished: {
|
|
var rc = scanResult
|
|
if (rc === "") { // scanning interrupted
|
|
thePile.pop();
|
|
return;
|
|
}
|
|
root.forceActiveFocus();
|
|
sweeper.privKey = rc;
|
|
}
|
|
onAutostartSkipped: {
|
|
/*
|
|
* This page can be opened as part of the Intent feature,
|
|
* if it has then we need to set the privatekey and at
|
|
* the same time clear out the property to prepare for
|
|
* next usage.
|
|
*/
|
|
var fromIntent = Intent.sweepKey
|
|
if (fromIntent !== "") {
|
|
Intent.sweepKey = ""; // prepare for next usage.
|
|
sweeper.privKey = fromIntent;
|
|
}
|
|
}
|
|
}
|
|
|
|
SweepHandler {
|
|
id: sweeper
|
|
account: pay.portfolio.current
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: 6
|
|
|
|
Flowee.Label {
|
|
text: qsTr("Sweeping from address:")
|
|
font.bold: true
|
|
width: parent.width
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
wrapMode: Text.Wrap
|
|
}
|
|
Flowee.Label {
|
|
text: sweeper.sweepAddress
|
|
width: parent.width
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
fontSizeMode: Text.HorizontalFit
|
|
}
|
|
Flowee.Label {
|
|
id: coinsLabel
|
|
visible: sweeper.numOutputsFound > 0 || sweeper.prepared
|
|
text: qsTr("Found %1 coins on address.", "this is a simple number", sweeper.numOutputsFound).arg(sweeper.numOutputsFound)
|
|
}
|
|
Flowee.Label {
|
|
visible: sweeper.numTokensFound > 0
|
|
text: qsTr("Ignoring %1 tokens.", "Number of CashTokens", sweeper.numTokensFound).arg(sweeper.numTokensFound)
|
|
}
|
|
|
|
Flowee.Progressbar {
|
|
width: parent.width
|
|
visible: !sweeper.prepared && sweeper.error === SweepHandler.NoError
|
|
progress: sweeper.downloadProgress / 1000
|
|
}
|
|
|
|
Item {
|
|
id: busyIndicator
|
|
visible: !sweeper.prepared && sweeper.error === SweepHandler.NoError
|
|
width: 105
|
|
height: visible ? 105 : 1
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
RotationAnimation on rotation {
|
|
loops: Animation.Infinite
|
|
from: 0
|
|
to: 360
|
|
duration: 6000
|
|
running: busyIndicator.visible
|
|
}
|
|
|
|
Repeater {
|
|
model: 6
|
|
Item {
|
|
width: 105
|
|
height: 35
|
|
y: 35
|
|
Rectangle {
|
|
color: mainWindow.floweeGreen
|
|
width: 30
|
|
height: 30
|
|
x: 70
|
|
radius: 15
|
|
}
|
|
rotation: 360 / 6 * index
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
color: mainWindow.errorRedBg
|
|
width: parent.width
|
|
height: sweepErrorLabel.text === "" ? 0 : sweepErrorLabel.height + 20
|
|
radius: 6
|
|
Flowee.Label {
|
|
id: sweepErrorLabel
|
|
width: parent.width - 20
|
|
wrapMode: Text.Wrap
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
anchors.centerIn: parent
|
|
text: {
|
|
var err = sweeper.error;
|
|
if (err === SweepHandler.InvalidInput)
|
|
return qsTr("Failed to understand QR");
|
|
if (err === SweepHandler.NoBackendFound)
|
|
return "No indexing servers found";
|
|
if (err === SweepHandler.FileError)
|
|
return "File not found error"; // not translated as this should never happen.
|
|
if (err === SweepHandler.DataInconsistency)
|
|
return qsTr("Indexer results invalid. Please try again.");
|
|
|
|
if (err === SweepHandler.NoError)
|
|
return "";
|
|
}
|
|
}
|
|
}
|
|
|
|
Flowee.BitcoinAmountLabel {
|
|
font.pixelSize: coinsLabel.font.pixelSize * 1.2
|
|
visible: sweeper.prepared
|
|
value: sweeper.sweepTotal
|
|
anchors.right: parent.right
|
|
}
|
|
}
|
|
|
|
Mobile.AccountSelectorWidget {
|
|
id: walletSelector
|
|
visible: !portfolio.singleAccountSetup
|
|
y: 320
|
|
onSelectedAccountChanged: sweeper.account = selectedAccount
|
|
}
|
|
Flowee.Label {
|
|
visible: walletSelector.visible
|
|
anchors.bottom: walletSelector.top
|
|
text: qsTr("Transfer to:")
|
|
}
|
|
|
|
Mobile.SlideToApprove {
|
|
id: slideToApprove
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 10
|
|
width: parent.width
|
|
enabled: sweeper.prepared && sweeper.numOutputsFound > 0
|
|
visible: !sweeper.account.needsPinToOpen
|
|
onActivated: {
|
|
root.hideHeader = true;
|
|
background.y = 0;
|
|
background.opacity = 1;
|
|
sweeper.markUserApproved();
|
|
}
|
|
}
|
|
|
|
QQC2.Control {
|
|
id: broadcastFeedback
|
|
anchors.leftMargin: -10 // go against the margins Page gave us to show more fullscreen.
|
|
anchors.rightMargin: -10
|
|
anchors.fill: parent
|
|
font.pixelSize: root.font.pixelSize * 1.2
|
|
|
|
property int status: sweeper.broadcastStatus
|
|
property double bitcoinAmount: sweeper.sweepTotal
|
|
property int fiatAmount: bitcoinAmount / 100000000 * Fiat.price
|
|
property string targetAddress: sweeper.targetAddress
|
|
|
|
states: [
|
|
State {
|
|
name: "notStarted"
|
|
when: broadcastFeedback.status === FloweePay.NotStarted
|
|
},
|
|
State {
|
|
name: "preparing"
|
|
when: broadcastFeedback.status === FloweePay.TxOffered
|
|
PropertyChanges { target: progressIcon; sweepAngle: 90 }
|
|
},
|
|
State {
|
|
name: "sent1" // sent to only one peer
|
|
extend: "preparing"
|
|
when: broadcastFeedback.status === FloweePay.TxSent1
|
|
PropertyChanges { target: progressIcon; sweepAngle: 150 }
|
|
PropertyChanges { target: statusLabel; text: qsTr("Sending Payment") }
|
|
},
|
|
State {
|
|
name: "waiting" // waiting for possible rejection.
|
|
when: broadcastFeedback.status === FloweePay.TxWaiting
|
|
extend: "preparing"
|
|
PropertyChanges { target: progressIcon; sweepAngle: 320 }
|
|
},
|
|
State {
|
|
name: "success" // no reject, great success
|
|
when: broadcastFeedback.status === FloweePay.TxBroadcastSuccess
|
|
extend: "preparing"
|
|
PropertyChanges { target: progressIcon
|
|
sweepAngle: 320
|
|
startAngle: -20
|
|
}
|
|
PropertyChanges { target: progressIcon; showCheck: true }
|
|
PropertyChanges { target: statusLabel; text: qsTr("Payment Sent") }
|
|
},
|
|
State {
|
|
name: "rejected" // a peer didn't like our tx
|
|
when: broadcastFeedback.status === FloweePay.TxRejected
|
|
extend: "preparing"
|
|
PropertyChanges { target: background; color: "#7f0000" }
|
|
PropertyChanges { target: progressIcon; opacity: 0 }
|
|
PropertyChanges { target: statusLabel; text: qsTr("Failed") }
|
|
PropertyChanges { target: errorLabel; text: qsTr("Transaction rejected by network") }
|
|
}
|
|
]
|
|
|
|
Rectangle {
|
|
id: background
|
|
width: parent.width
|
|
height: parent.height
|
|
opacity: 0
|
|
visible: opacity > 0
|
|
color: "#7bb688"
|
|
y: height + 2
|
|
MouseArea {
|
|
anchors.fill: parent // eat all mouse events.
|
|
}
|
|
Flowee.Label {
|
|
id: statusLabel
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
font.bold: true
|
|
y: 30
|
|
color: "#e8e8e8"
|
|
}
|
|
|
|
Flowee.ProgressCheckIcon {
|
|
id: progressIcon
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.top: statusLabel.bottom
|
|
anchors.topMargin: 20
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
anchors.top: progressIcon.bottom
|
|
anchors.topMargin: 6
|
|
spacing: 6
|
|
Rectangle {
|
|
id: errorTextPane
|
|
visible: errorLabel.text !== ""
|
|
color: mainWindow.errorRedBg
|
|
radius: 10
|
|
width: parent.width
|
|
height: errorLabel.height + 20
|
|
|
|
Flowee.Label {
|
|
id: errorLabel
|
|
wrapMode: Text.Wrap
|
|
x: 10
|
|
y: 10
|
|
width: parent.width - 20
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
}
|
|
}
|
|
Flowee.Label {
|
|
id: fiatLabel
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
color: statusLabel.color
|
|
font.pixelSize: statusLabel.font.pixelSize * 2.5
|
|
text: Fiat.formattedPrice(broadcastFeedback.fiatAmount)
|
|
visible: Fiat.price !== 0
|
|
}
|
|
|
|
Flowee.BitcoinAmountLabel {
|
|
id: cryptoAmount
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
value: broadcastFeedback.bitcoinAmount
|
|
colorize: false
|
|
showFiat: false
|
|
color: statusLabel.color
|
|
}
|
|
Item { width: 10; height: 10 } // spacer
|
|
|
|
Flowee.Label {
|
|
id: addressLabel
|
|
color: statusLabel.color
|
|
visible: broadcastFeedback.targetAddress !== ""
|
|
width: parent.width - 20
|
|
x: 10
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
wrapMode: Text.Wrap
|
|
text: qsTr("The payment has been sent to:", "Followed by the address")
|
|
}
|
|
Flowee.Label {
|
|
color: statusLabel.color
|
|
visible: broadcastFeedback.targetAddress !== ""
|
|
width: parent.width - 20
|
|
x: 10
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
text: broadcastFeedback.targetAddress
|
|
fontSizeMode: Text.HorizontalFit
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: closeButtonBg
|
|
color: statusLabel.color
|
|
radius: 10
|
|
width: parent.width - 20
|
|
x: 10
|
|
height: closeButtonLabel.height + 25
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: 10
|
|
Flowee.Label {
|
|
id: closeButtonLabel
|
|
text: qsTr("Close")
|
|
anchors.centerIn: parent
|
|
color: background.color
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
anchors.margins: -10
|
|
focus: true
|
|
onClicked: {
|
|
var mainView = thePile.get(0);
|
|
mainView.currentIndex = 0; // go to the 'main' tab.
|
|
thePile.pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
Behavior on opacity { NumberAnimation { } }
|
|
Behavior on y { NumberAnimation { } }
|
|
Behavior on color { ColorAnimation { } }
|
|
}
|
|
}
|
|
}
|