2021-01-05 00:25:23 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
|
|
|
|
* Copyright (C) 2020-2021 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/>.
|
|
|
|
|
*/
|
2021-05-23 00:27:34 +02:00
|
|
|
import QtQuick 2.11
|
|
|
|
|
import QtQuick.Controls 2.11
|
|
|
|
|
import QtQuick.Layouts 1.11
|
|
|
|
|
import QtQuick.Shapes 1.11 // for shape-path
|
2021-01-05 00:25:23 +01:00
|
|
|
|
|
|
|
|
import Flowee.org.pay 1.0
|
|
|
|
|
|
|
|
|
|
Pane {
|
|
|
|
|
id: receivePane
|
2021-01-16 17:01:42 +01:00
|
|
|
|
2021-04-20 19:30:56 +02:00
|
|
|
property QtObject account: portfolio.current
|
2021-04-21 17:17:47 +02:00
|
|
|
property QtObject request: null
|
|
|
|
|
onAccountChanged: {
|
|
|
|
|
if (account === null)
|
|
|
|
|
return;
|
|
|
|
|
if (request === null)
|
|
|
|
|
request = account.createPaymentRequest(receivePane)
|
|
|
|
|
else
|
|
|
|
|
request.switchAccount(portfolio.current);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-21 18:15:14 +02:00
|
|
|
function reset() {
|
2021-05-05 12:42:26 +02:00
|
|
|
if (request.saveState !== PaymentRequest.Stored)
|
|
|
|
|
request.destroy();
|
|
|
|
|
request = account.createPaymentRequest(receivePane)
|
2021-04-21 18:15:14 +02:00
|
|
|
description.text = "";
|
2021-05-01 13:15:39 +02:00
|
|
|
bitcoinValueField.reset();
|
2021-04-21 18:15:14 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-21 17:17:47 +02:00
|
|
|
Label {
|
|
|
|
|
id: instructions
|
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
|
anchors.topMargin: 20
|
2021-04-30 17:54:26 +02:00
|
|
|
text: qsTr("Share your QR code or copy address to receive")
|
|
|
|
|
opacity: 0.5
|
2021-04-21 17:17:47 +02:00
|
|
|
}
|
2021-01-05 00:25:23 +01:00
|
|
|
|
|
|
|
|
Image {
|
|
|
|
|
id: qrCode
|
2021-04-21 20:01:58 +02:00
|
|
|
width: height
|
|
|
|
|
height: {
|
|
|
|
|
var h = parent.height - 220;
|
|
|
|
|
return Math.min(h, 256)
|
|
|
|
|
}
|
2021-01-05 00:25:23 +01:00
|
|
|
source: "image://qr/" + receivePane.request.qr
|
|
|
|
|
smooth: false
|
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
2021-04-21 17:17:47 +02:00
|
|
|
anchors.top: instructions.bottom
|
2021-01-06 15:26:46 +01:00
|
|
|
anchors.topMargin: 20
|
2021-01-18 14:27:24 +01:00
|
|
|
opacity: receivePane.request.state === PaymentRequest.Unpaid ? 1: 0
|
2021-01-05 00:25:23 +01:00
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
onClicked: {
|
|
|
|
|
Flowee.copyToClipboard(receivePane.request.qr)
|
2021-02-05 16:53:42 +01:00
|
|
|
clipboardFeedback.opacity = 1
|
2021-01-06 15:26:46 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
2021-01-18 14:27:24 +01:00
|
|
|
id: clipboardFeedback
|
2021-01-06 15:26:46 +01:00
|
|
|
opacity: 0
|
2021-04-29 16:31:32 +02:00
|
|
|
width: feedbackText.width + 20
|
2021-01-06 15:26:46 +01:00
|
|
|
height: feedbackText.height + 20
|
|
|
|
|
radius: 10
|
|
|
|
|
color: Flowee.useDarkSkin ? "#333" : "#ddd"
|
2021-04-29 15:24:01 +02:00
|
|
|
anchors.top: parent.bottom
|
|
|
|
|
anchors.topMargin: -13
|
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
2021-01-06 15:26:46 +01:00
|
|
|
|
|
|
|
|
Label {
|
|
|
|
|
id: feedbackText
|
|
|
|
|
x: 10
|
|
|
|
|
y: 10
|
|
|
|
|
text: qsTr("Copied to clipboard")
|
|
|
|
|
wrapMode: Text.WordWrap
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Behavior on opacity { OpacityAnimator {} }
|
|
|
|
|
|
|
|
|
|
/// after 10 seconds, remove feedback.
|
|
|
|
|
Timer {
|
|
|
|
|
interval: 10000
|
2021-01-18 14:27:24 +01:00
|
|
|
running: clipboardFeedback.opacity === 1
|
|
|
|
|
onTriggered: clipboardFeedback.opacity = 0
|
2021-01-05 00:25:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-01-18 14:27:24 +01:00
|
|
|
|
|
|
|
|
Behavior on opacity { OpacityAnimator {} }
|
2021-01-05 00:25:23 +01:00
|
|
|
}
|
|
|
|
|
|
2021-01-18 14:27:24 +01:00
|
|
|
// the "payment received" screen.
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.top: parent.top
|
|
|
|
|
anchors.topMargin: 20
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.bottom: qrCode.bottom
|
|
|
|
|
radius: 10
|
|
|
|
|
gradient: Gradient {
|
|
|
|
|
GradientStop {
|
|
|
|
|
position: 0.6
|
|
|
|
|
color: {
|
|
|
|
|
var state = receivePane.request.state;
|
|
|
|
|
if (state === PaymentRequest.PaymentSeen || state === PaymentRequest.Unpaid)
|
|
|
|
|
return receivePane.palette.base
|
|
|
|
|
if (state === PaymentRequest.DoubleSpentSeen)
|
|
|
|
|
return "#640e0f" // red
|
|
|
|
|
return "#3e8b4e" // in all other cases: green
|
|
|
|
|
}
|
|
|
|
|
Behavior on color { ColorAnimation {} }
|
|
|
|
|
}
|
|
|
|
|
GradientStop {
|
|
|
|
|
position: 0.1
|
2021-04-21 17:17:47 +02:00
|
|
|
color: receivePane.palette.base
|
2021-01-18 14:27:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
opacity: receivePane.request.state === PaymentRequest.Unpaid ? 0: 1
|
|
|
|
|
|
|
|
|
|
// animating timer to indicate our checking the security of the transaction.
|
|
|
|
|
// (i.e. waiting for the double spent proof)
|
|
|
|
|
Item {
|
|
|
|
|
id: feedback
|
|
|
|
|
width: 160
|
|
|
|
|
height: 160
|
|
|
|
|
y: (parent.height - height) / 3 * 2
|
|
|
|
|
visible: receivePane.request.state !== PaymentRequest.DoubleSpentSeen
|
|
|
|
|
Shape {
|
|
|
|
|
id: circleShape
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
opacity: progressCircle.sweepAngle === 340 ? 0 : 1
|
|
|
|
|
x: 40
|
|
|
|
|
ShapePath {
|
|
|
|
|
strokeWidth: 20
|
|
|
|
|
strokeColor: "#9ea0b0"
|
|
|
|
|
fillColor: "transparent"
|
|
|
|
|
capStyle: ShapePath.RoundCap
|
|
|
|
|
startX: 100; startY: 10
|
|
|
|
|
|
|
|
|
|
PathAngleArc {
|
|
|
|
|
id: progressCircle
|
|
|
|
|
centerX: 80
|
|
|
|
|
centerY: 80
|
|
|
|
|
radiusX: 70; radiusY: 70
|
|
|
|
|
startAngle: -80
|
|
|
|
|
sweepAngle: receivePane.request.state === PaymentRequest.Unpaid ? 0: 340
|
|
|
|
|
|
|
|
|
|
Behavior on sweepAngle { NumberAnimation { duration: Flowee.dspTimeout } }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Label {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
text: qsTr("Checking") // checking security
|
|
|
|
|
}
|
|
|
|
|
Behavior on opacity { OpacityAnimator {} }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Label {
|
|
|
|
|
color: "green"
|
|
|
|
|
text: "✔"
|
|
|
|
|
opacity: 1 - circleShape.opacity
|
|
|
|
|
font.pixelSize: 130
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Label {
|
2021-01-20 11:34:15 +01:00
|
|
|
id: feedbackLabel
|
2021-01-18 14:27:24 +01:00
|
|
|
text: {
|
|
|
|
|
var s = receivePane.request.state;
|
|
|
|
|
if (s === PaymentRequest.DoubleSpentSeen)
|
2021-01-20 11:34:15 +01:00
|
|
|
// double-spent-proof received
|
|
|
|
|
return qsTr("Transaction high risk")
|
2021-01-18 14:27:24 +01:00
|
|
|
if (s === PaymentRequest.PaymentSeen)
|
|
|
|
|
return qsTr("Payment Seen")
|
|
|
|
|
if (s === PaymentRequest.PaymentSeenOk)
|
|
|
|
|
return qsTr("Payment Accepted")
|
|
|
|
|
if (s === PaymentRequest.Confirmed)
|
|
|
|
|
return qsTr("Payment Settled")
|
2021-01-20 11:34:15 +01:00
|
|
|
return "INTERNAL ERROR";
|
2021-01-18 14:27:24 +01:00
|
|
|
}
|
2021-02-04 19:21:45 +01:00
|
|
|
width: parent.width - 40
|
2021-01-18 14:27:24 +01:00
|
|
|
anchors.verticalCenter: feedback.verticalCenter
|
|
|
|
|
anchors.left: feedback.visible ? feedback.right : parent.left
|
|
|
|
|
anchors.leftMargin: 20
|
2021-02-04 19:21:45 +01:00
|
|
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
2021-01-18 14:27:24 +01:00
|
|
|
font.pointSize: 20
|
|
|
|
|
}
|
2021-01-20 11:34:15 +01:00
|
|
|
Label {
|
|
|
|
|
visible: receivePane.request.state === PaymentRequest.DoubleSpentSeen
|
|
|
|
|
anchors.top: feedbackLabel.bottom
|
2021-02-04 19:21:45 +01:00
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 10
|
|
|
|
|
width: parent.width - 20
|
|
|
|
|
horizontalAlignment: Qt.AlignRight
|
2021-01-20 11:34:15 +01:00
|
|
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
|
|
|
|
text: qsTr("Instant payment failed. Wait for confirmation. (double spent proof received)")
|
|
|
|
|
}
|
2021-01-18 14:27:24 +01:00
|
|
|
|
|
|
|
|
Behavior on opacity { OpacityAnimator {} }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// entry-fields
|
2021-01-05 00:25:23 +01:00
|
|
|
GridLayout {
|
|
|
|
|
id: grid
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.top: qrCode.bottom
|
2021-01-06 15:26:46 +01:00
|
|
|
anchors.topMargin: 30
|
2021-01-05 00:25:23 +01:00
|
|
|
columns: 2
|
|
|
|
|
Label {
|
2021-01-29 10:27:24 +01:00
|
|
|
text: qsTr("Description") + ":"
|
2021-01-05 00:25:23 +01:00
|
|
|
}
|
|
|
|
|
TextField {
|
|
|
|
|
id: description
|
|
|
|
|
Layout.fillWidth: true
|
2021-01-18 14:27:24 +01:00
|
|
|
enabled: receivePane.request.state === PaymentRequest.Unpaid
|
2021-01-05 00:25:23 +01:00
|
|
|
onTextChanged: receivePane.request.message = text
|
|
|
|
|
}
|
2021-01-06 15:26:46 +01:00
|
|
|
|
2021-01-05 00:25:23 +01:00
|
|
|
Label {
|
|
|
|
|
id: payAmount
|
2021-01-29 10:27:24 +01:00
|
|
|
text: qsTr("Amount") + ":"
|
2021-01-05 00:25:23 +01:00
|
|
|
}
|
2021-05-08 00:03:07 +02:00
|
|
|
RowLayout {
|
|
|
|
|
spacing: 10
|
|
|
|
|
BitcoinValueField {
|
|
|
|
|
id: bitcoinValueField
|
|
|
|
|
fontPtSize: payAmount.font.pointSize
|
|
|
|
|
enabled: receivePane.request.state === PaymentRequest.Unpaid
|
|
|
|
|
onValueChanged: receivePane.request.amount = value
|
|
|
|
|
}
|
|
|
|
|
Label {
|
|
|
|
|
Layout.alignment: Qt.AlignBaseline
|
|
|
|
|
anchors.baselineOffset: bitcoinValueField.baselineOffset
|
|
|
|
|
text: Fiat.formattedPrice(bitcoinValueField.value, Fiat.price)
|
|
|
|
|
}
|
2021-01-05 00:25:23 +01:00
|
|
|
}
|
2021-01-06 15:26:46 +01:00
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
Layout.columnSpan: 2
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Pane {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
}
|
2021-01-06 23:15:54 +01:00
|
|
|
Button {
|
|
|
|
|
text: qsTr("Remember", "payment request")
|
2021-01-20 11:37:27 +01:00
|
|
|
visible: receivePane.request.state === PaymentRequest.Unpaid || receivePane.request.state === PaymentRequest.DoubleSpentSeen
|
2021-01-06 23:15:54 +01:00
|
|
|
onClicked: {
|
2021-05-05 12:42:26 +02:00
|
|
|
receivePane.request.stored = true
|
2021-04-21 20:12:58 +02:00
|
|
|
reset();
|
2021-01-06 23:15:54 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-01-06 15:26:46 +01:00
|
|
|
Button {
|
2021-05-05 12:42:26 +02:00
|
|
|
text: receivePane.request.state === PaymentRequest.Unpaid ? qsTr("Clear") : qsTr("Done")
|
|
|
|
|
onClicked: {
|
|
|
|
|
reset();
|
|
|
|
|
}
|
2021-01-06 15:26:46 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-01-05 00:25:23 +01:00
|
|
|
}
|
2021-04-21 20:01:58 +02:00
|
|
|
|
|
|
|
|
Flow {
|
|
|
|
|
width: parent.width
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
spacing: 10
|
|
|
|
|
Repeater {
|
|
|
|
|
model: portfolio.current.paymentRequests
|
|
|
|
|
delegate: Rectangle {
|
|
|
|
|
width: 70
|
|
|
|
|
height: width
|
|
|
|
|
radius: 25
|
|
|
|
|
clip: true
|
|
|
|
|
border.width: 6
|
|
|
|
|
border.color: {
|
|
|
|
|
var state = modelData.state;
|
|
|
|
|
if (state === PaymentRequest.Unpaid)
|
|
|
|
|
return "#888888"
|
|
|
|
|
if (state === PaymentRequest.PaymentSeen)
|
|
|
|
|
return "yellow"
|
|
|
|
|
if (state === PaymentRequest.DoubleSpentSeen)
|
|
|
|
|
return "red"
|
|
|
|
|
// in all other cases:
|
|
|
|
|
return "green"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// don't show the one we are editing
|
2021-05-05 12:42:26 +02:00
|
|
|
visible: modelData.saveState === PaymentRequest.Stored
|
2021-04-21 20:01:58 +02:00
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
text: modelData.message
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
anchors.fill: parent
|
2021-04-21 20:12:58 +02:00
|
|
|
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
2021-04-21 20:01:58 +02:00
|
|
|
onClicked: paymentContextMenu.popup()
|
|
|
|
|
Menu {
|
|
|
|
|
id: paymentContextMenu
|
|
|
|
|
MenuItem {
|
|
|
|
|
text: qsTr("Delete")
|
2021-05-05 14:13:40 +02:00
|
|
|
onTriggered: modelData.stored = false;
|
2021-04-21 20:01:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-05 00:25:23 +01:00
|
|
|
}
|