2023-03-10 22:16:37 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2025-02-04 21:20:35 +01:00
|
|
|
* Copyright (C) 2022-2025 Tom Zander <tom@flowee.org>
|
2023-03-10 22:16:37 +01:00
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*/
|
2023-06-12 15:26:31 +02:00
|
|
|
import QtQuick;
|
|
|
|
|
import QtQuick.Layouts;
|
|
|
|
|
import QtQuick.Controls as QQC2;
|
|
|
|
|
import "../Flowee" as Flowee;
|
|
|
|
|
import "../mobile";
|
2023-03-10 22:16:37 +01:00
|
|
|
import Flowee.org.pay;
|
|
|
|
|
|
2023-03-11 22:05:15 +01:00
|
|
|
|
2023-03-10 22:16:37 +01:00
|
|
|
Page {
|
|
|
|
|
id: root
|
2023-03-13 10:36:27 +01:00
|
|
|
headerText: qsTr("Build Transaction")
|
2023-03-10 22:16:37 +01:00
|
|
|
|
2023-03-12 17:21:02 +01:00
|
|
|
Item { // data and non-visible stuff for this page
|
|
|
|
|
/*
|
|
|
|
|
* Components embedded in this file:
|
|
|
|
|
* Editors:
|
|
|
|
|
destinationEditPage
|
2023-03-12 14:26:13 +01:00
|
|
|
|
2023-03-12 17:21:02 +01:00
|
|
|
editors can use the property 'paymentDetail'
|
|
|
|
|
|
|
|
|
|
* ListItems:
|
|
|
|
|
destinationFields
|
|
|
|
|
|
|
|
|
|
list items have the property 'edit' to link to Editors
|
|
|
|
|
property Component edit: [something]
|
|
|
|
|
list items can use the property 'paymentDetail'
|
|
|
|
|
|
|
|
|
|
* Other:
|
|
|
|
|
paymentDetailSelector
|
2023-03-13 11:52:47 +01:00
|
|
|
paymentInfoPage
|
2023-03-12 17:21:02 +01:00
|
|
|
|
|
|
|
|
* To open editors or list items, we use the pushToThePile() function.
|
|
|
|
|
*/
|
2023-03-10 22:16:37 +01:00
|
|
|
Payment {
|
|
|
|
|
id: payment
|
|
|
|
|
account: portfolio.current
|
|
|
|
|
fiatPrice: Fiat.price
|
2025-02-17 16:23:24 +01:00
|
|
|
|
|
|
|
|
onApprovedByUser: {
|
|
|
|
|
thePile.currentItem.hideHeader = true;
|
|
|
|
|
broadcastPage.start();
|
|
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
2023-03-13 12:53:33 +01:00
|
|
|
Flowee.Dialog {
|
|
|
|
|
id: errorDialog
|
|
|
|
|
standardButtons: QQC2.DialogButtonBox.Close
|
|
|
|
|
title: qsTr("Building Error", "error during build")
|
|
|
|
|
text: payment.error
|
|
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
|
|
|
|
|
// Extra page to create new details.
|
|
|
|
|
Component {
|
|
|
|
|
id: paymentDetailSelector
|
|
|
|
|
Page {
|
|
|
|
|
headerText: qsTr("Add Payment Detail", "page title")
|
|
|
|
|
Flickable {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
contentHeight: col.height
|
|
|
|
|
Column {
|
|
|
|
|
id: col
|
|
|
|
|
width: parent.width
|
|
|
|
|
TextButton {
|
2023-03-12 17:21:02 +01:00
|
|
|
text: qsTr("Add Destination")
|
|
|
|
|
subtext: qsTr("an address to send money to")
|
2023-03-10 22:16:37 +01:00
|
|
|
onClicked: {
|
2023-03-11 22:05:15 +01:00
|
|
|
var detail = payment.addExtraOutput();
|
2023-03-10 22:16:37 +01:00
|
|
|
thePile.pop();
|
2023-03-12 14:26:13 +01:00
|
|
|
pushToThePile(destinationEditPage,
|
|
|
|
|
detail);
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 11:52:47 +01:00
|
|
|
// show info about payment and allow broadcast
|
|
|
|
|
Component {
|
|
|
|
|
id: paymentInfoPage
|
|
|
|
|
Page {
|
|
|
|
|
id: root
|
|
|
|
|
headerText: qsTr("Confirm Sending", "confirm we want to send the transaction")
|
|
|
|
|
|
|
|
|
|
Flickable {
|
|
|
|
|
anchors.top: parent.top
|
|
|
|
|
anchors.bottom: slideToApprove.top
|
|
|
|
|
width: parent.width
|
|
|
|
|
contentHeight: col.implicitHeight
|
|
|
|
|
clip: true
|
|
|
|
|
|
|
|
|
|
Column {
|
|
|
|
|
id: col
|
|
|
|
|
width: parent.width
|
|
|
|
|
spacing: 10
|
|
|
|
|
|
|
|
|
|
// the below is all very technical stuff that most people don't care about.
|
|
|
|
|
// it was easy, but its not useful.
|
|
|
|
|
// So, TODO insert here actually useful to end users details like amount sent,
|
|
|
|
|
// change address used. Etc.
|
|
|
|
|
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
text: qsTr("TXID") + ":"
|
|
|
|
|
}
|
|
|
|
|
Flowee.LabelWithClipboard {
|
|
|
|
|
id: txid
|
|
|
|
|
text: payment.txid
|
|
|
|
|
font.pixelSize: root.font.pixelSize * 0.8
|
|
|
|
|
width: parent.width
|
|
|
|
|
// Change the color when the portfolio changed since 'prepare' was clicked.
|
|
|
|
|
menuText: qsTr("Copy transaction-ID")
|
|
|
|
|
}
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
text: qsTr("Fee") + ":"
|
|
|
|
|
}
|
|
|
|
|
Flowee.BitcoinAmountLabel {
|
|
|
|
|
value: payment.assignedFee
|
|
|
|
|
colorize: false
|
|
|
|
|
}
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
text: qsTr("Transaction size") + ":"
|
|
|
|
|
}
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
text: qsTr("%1 bytes").arg(payment.txSize)
|
|
|
|
|
}
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
text: qsTr("Fee per byte") + ":"
|
|
|
|
|
}
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
text: {
|
|
|
|
|
var rc = payment.assignedFee / payment.txSize;
|
|
|
|
|
var fee = rc.toFixed(3); // no more than 3 numbers behind the separator
|
|
|
|
|
fee = (fee * 1.0).toString(); // remove trailing zero's (1.000 => 1)
|
|
|
|
|
return qsTr("%1 sat/byte", "fee").arg(fee);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SlideToApprove {
|
|
|
|
|
id: slideToApprove
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
anchors.bottomMargin: 10
|
|
|
|
|
width: parent.width
|
2023-07-07 09:41:04 +02:00
|
|
|
onActivated: {
|
2023-09-06 21:07:30 +02:00
|
|
|
payment.markUserApproved()
|
2025-03-16 22:14:20 +01:00
|
|
|
broadcastPage.personalNote = payment.userComment
|
2023-07-07 09:41:04 +02:00
|
|
|
thePile.pop(); // the broadcast feedback is on the main screen.
|
2023-03-13 11:52:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 22:16:37 +01:00
|
|
|
// listitem of PaymentDetail showing payment-destination
|
|
|
|
|
Component {
|
|
|
|
|
id: destinationFields
|
|
|
|
|
Column {
|
|
|
|
|
property Component edit: destinationEditPage
|
|
|
|
|
width: parent.width
|
|
|
|
|
spacing: 6
|
|
|
|
|
|
|
|
|
|
/* This page just shows the results, editing is done
|
|
|
|
|
* on its own page.
|
|
|
|
|
*/
|
2023-05-31 15:47:14 +02:00
|
|
|
PageTitledBox {
|
2023-03-10 22:16:37 +01:00
|
|
|
width: parent.width
|
2023-05-31 15:47:14 +02:00
|
|
|
title: qsTr("Destination")
|
|
|
|
|
|
|
|
|
|
Flowee.LabelWithClipboard {
|
|
|
|
|
width: parent.width
|
|
|
|
|
font.italic: paymentDetail.address === ""
|
|
|
|
|
text: {
|
|
|
|
|
var s = paymentDetail.niceAddress
|
2023-06-04 19:20:18 +02:00
|
|
|
if (s === "") {
|
|
|
|
|
if (paymentDetail.address === "") // the user-specified text is empty
|
2024-10-14 12:55:51 +02:00
|
|
|
return qsTr("unset", "indication of desination not being set");
|
2023-06-04 19:20:18 +02:00
|
|
|
return qsTr("invalid", "address is not correct");
|
|
|
|
|
}
|
2023-05-31 15:47:14 +02:00
|
|
|
return s;
|
2023-03-12 17:21:02 +01:00
|
|
|
}
|
2023-05-31 15:47:14 +02:00
|
|
|
color: addressInfo.addressOk || paymentDetail.address === ""
|
|
|
|
|
? palette.windowText : mainWindow.errorRed
|
|
|
|
|
font.pixelSize: mainWindow.font.pixelSize * 0.9
|
|
|
|
|
menuText: qsTr("Copy Address")
|
|
|
|
|
clipboardText: paymentDetail.formattedTarget // the one WITH bitcoincash:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Flowee.AddressInfoWidget {
|
|
|
|
|
id: addressInfo
|
|
|
|
|
width: parent.width
|
|
|
|
|
addressType: Pay.identifyString(paymentDetail.address);
|
|
|
|
|
}
|
|
|
|
|
Flowee.BitcoinAmountLabel {
|
|
|
|
|
value: paymentDetail.paymentAmount
|
|
|
|
|
colorize: false
|
2023-03-12 17:21:02 +01:00
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-12 14:26:13 +01:00
|
|
|
/*
|
|
|
|
|
* The different payment things work with a 'paymentDetail'
|
|
|
|
|
* and since we push those into the global stack, they get
|
|
|
|
|
* loaded and initialized first, only secondly we set the
|
|
|
|
|
* property paymentDetail on them.
|
|
|
|
|
*
|
|
|
|
|
* This means we either get a load of errors dereferencing a null
|
|
|
|
|
* object, or we need to alter a lot of code to account for that.
|
|
|
|
|
*
|
|
|
|
|
* This is the alternative solution: we add a layer of indirection
|
|
|
|
|
* and set the property on the loader and the item in the loader
|
|
|
|
|
* will simply find the paymentDetail present in the context in
|
|
|
|
|
* which it has been loaded.
|
|
|
|
|
*/
|
|
|
|
|
Component {
|
|
|
|
|
id: loaderForPayments
|
|
|
|
|
FocusScope {
|
|
|
|
|
property alias paymentDetail: loader2.paymentDetail
|
|
|
|
|
property Component sourceComponent: undefined
|
|
|
|
|
function takeFocus() {
|
|
|
|
|
// this is also present in 'page', and called from thePile
|
|
|
|
|
forceActiveFocus();
|
|
|
|
|
}
|
|
|
|
|
Loader {
|
|
|
|
|
property QtObject paymentDetail: null
|
|
|
|
|
id: loader2
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
onLoaded: item.takeFocus();
|
|
|
|
|
}
|
|
|
|
|
function load() {
|
|
|
|
|
if (paymentDetail != null && typeof sourceComponent != "undefined") {
|
|
|
|
|
loader2.sourceComponent = sourceComponent
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
onPaymentDetailChanged: load();
|
|
|
|
|
onSourceComponentChanged: load();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 22:16:37 +01:00
|
|
|
Component {
|
|
|
|
|
id: destinationEditPage
|
2025-02-04 18:07:46 +01:00
|
|
|
DestinationEditPage { }
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
2023-06-30 22:27:27 +02:00
|
|
|
/*
|
|
|
|
|
* A helper page that allows unlocking an account prior to paying from it.
|
|
|
|
|
*/
|
|
|
|
|
Component {
|
|
|
|
|
id: unlockInPage
|
|
|
|
|
Page {
|
|
|
|
|
headerText: payment.account.name
|
|
|
|
|
UnlockWalletPanel {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: 10
|
|
|
|
|
|
|
|
|
|
account: payment.account
|
|
|
|
|
Connections {
|
|
|
|
|
target: payment.account
|
|
|
|
|
function onIsDecryptedChanged() {
|
|
|
|
|
if (payment.account.isDecrypted)
|
|
|
|
|
thePile.pop()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-31 18:53:18 +02:00
|
|
|
}
|
2023-03-12 17:21:02 +01:00
|
|
|
// check the comment at loaderForPayments to understand this one
|
|
|
|
|
function pushToThePile(componentId, detail) {
|
|
|
|
|
thePile.push(loaderForPayments,
|
2024-10-01 19:57:17 +02:00
|
|
|
{ "paymentDetail": detail,
|
2023-03-12 17:21:02 +01:00
|
|
|
"sourceComponent": componentId }
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 22:16:37 +01:00
|
|
|
Flickable {
|
|
|
|
|
id: contentArea
|
|
|
|
|
anchors.fill: parent
|
2023-03-12 20:31:37 +01:00
|
|
|
contentHeight: mainColumn.height + 10
|
2023-03-10 22:16:37 +01:00
|
|
|
contentWidth: width
|
|
|
|
|
clip: true
|
|
|
|
|
Column {
|
|
|
|
|
id: mainColumn
|
2023-03-12 20:31:37 +01:00
|
|
|
y: 10
|
2023-03-10 22:16:37 +01:00
|
|
|
width: parent.width
|
2024-10-01 21:14:54 +02:00
|
|
|
spacing: 10
|
2023-03-12 17:21:02 +01:00
|
|
|
|
2023-05-18 21:52:51 +02:00
|
|
|
AccountSelectorWidget {
|
|
|
|
|
visible: !portfolio.singleAccountSetup
|
|
|
|
|
}
|
2023-03-13 10:36:27 +01:00
|
|
|
|
2023-05-18 21:52:51 +02:00
|
|
|
VisualSeparator {
|
|
|
|
|
visible: !portfolio.singleAccountSetup
|
|
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
|
model: payment.details
|
|
|
|
|
delegate: Item {
|
|
|
|
|
id: listItem
|
|
|
|
|
width: mainColumn.width
|
2023-03-12 17:21:02 +01:00
|
|
|
height: loader.height + 20 + (index <= 1 ? (dragInstructions.height + 20) : 0)
|
2023-03-10 22:16:37 +01:00
|
|
|
|
|
|
|
|
Loader {
|
|
|
|
|
id: loader
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: status === Loader.Ready ? item.implicitHeight : 0
|
2023-03-12 14:26:13 +01:00
|
|
|
property QtObject paymentDetail: modelData
|
2023-03-10 22:16:37 +01:00
|
|
|
sourceComponent: {
|
|
|
|
|
if (modelData.type === Payment.PayToAddress)
|
|
|
|
|
return destinationFields
|
|
|
|
|
if (modelData.type === Payment.InputSelector)
|
|
|
|
|
return inputFields
|
|
|
|
|
return null; // should never happen
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-11 13:28:29 +01:00
|
|
|
Item {
|
|
|
|
|
// an invisible item that is used to handle the drag events.
|
|
|
|
|
id: dragItem
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: parent.height
|
|
|
|
|
|
|
|
|
|
property bool editStarted: false
|
|
|
|
|
onXChanged: {
|
2024-10-01 19:57:17 +02:00
|
|
|
/* When we move, we move the icons etc with us. */
|
2023-03-11 13:28:29 +01:00
|
|
|
if (x < 0) {
|
|
|
|
|
leftArea.x = leftArea.width * -1 // out of screen
|
|
|
|
|
// moving left, opening the edit option
|
|
|
|
|
loader.x = x;
|
|
|
|
|
let a = x * -1;
|
|
|
|
|
editIcon.x = width - a + Math.max(0, a - editIcon.width);
|
|
|
|
|
if (!editStarted && x < -100) {
|
|
|
|
|
editStarted = true;
|
2023-03-12 14:26:13 +01:00
|
|
|
pushToThePile(loader.item.edit, modelData);
|
2023-03-11 13:28:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
editIcon.x = width;
|
|
|
|
|
// moving right, opening the trashcan option
|
|
|
|
|
let a = x;
|
|
|
|
|
let base = Math.min(40, a);
|
|
|
|
|
let rest = a - base;
|
|
|
|
|
deleteTimer.running = rest > 90;
|
|
|
|
|
if (rest < 90)
|
|
|
|
|
deleteTimer.deleteTriggered = false
|
|
|
|
|
leftBackground.opacity = Math.max(rest - 40) / 100;
|
|
|
|
|
rest = Math.min(60, rest) / 4; // slower
|
|
|
|
|
leftArea.x = leftArea.width * -1 + base + rest;
|
|
|
|
|
loader.x = base + rest;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DragHandler {
|
|
|
|
|
id: dragHandler
|
|
|
|
|
// property bool editStarted: false;
|
|
|
|
|
yAxis.enabled: false
|
|
|
|
|
xAxis.enabled: true
|
2023-03-12 17:21:02 +01:00
|
|
|
xAxis.maximum: index > 0 ? 200 : 0 // swipe left
|
2023-03-11 13:28:29 +01:00
|
|
|
xAxis.minimum: -140 // swipe right
|
|
|
|
|
onActiveChanged: {
|
|
|
|
|
if (active) {
|
|
|
|
|
parent.editStarted = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// take action on user releasing the drag.
|
|
|
|
|
if (deleteTimer.deleteTriggered) {
|
|
|
|
|
deleteTimer.deleteTriggered = false;
|
|
|
|
|
payment.remove(modelData);
|
|
|
|
|
}
|
|
|
|
|
parent.x = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 22:16:37 +01:00
|
|
|
// delete-detail interface
|
2023-03-11 13:28:29 +01:00
|
|
|
Item {
|
|
|
|
|
id: leftArea
|
2023-03-10 22:16:37 +01:00
|
|
|
width: 300
|
2023-03-12 17:21:02 +01:00
|
|
|
height: loader.height
|
2023-03-11 13:28:29 +01:00
|
|
|
x: -width;
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: leftBackground
|
|
|
|
|
opacity: 0
|
|
|
|
|
anchors.fill: parent
|
2023-04-18 22:09:45 +02:00
|
|
|
color: mainWindow.errorRedBg
|
2024-10-01 19:57:17 +02:00
|
|
|
radius: 6
|
|
|
|
|
Image {
|
|
|
|
|
id: trashcan
|
|
|
|
|
source: "./recycle" + (Pay.useDarkSkin ? "-light" : "") + ".svg"
|
|
|
|
|
width: 40
|
|
|
|
|
height: 40
|
|
|
|
|
y: 10
|
|
|
|
|
x: {
|
|
|
|
|
let newX = parent.width - width;
|
|
|
|
|
let moved = parent.x + parent.width;
|
|
|
|
|
let additional = Math.max(0, Math.min(moved - width, 8));
|
|
|
|
|
return newX - additional;
|
|
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-11 13:28:29 +01:00
|
|
|
|
|
|
|
|
Timer {
|
|
|
|
|
/*
|
|
|
|
|
The intention of this timer is that the user can't just swipe hard
|
|
|
|
|
and suddenly lose their data.
|
|
|
|
|
We need to have the user actually see the red for half a second
|
|
|
|
|
before we delete.
|
|
|
|
|
*/
|
|
|
|
|
id: deleteTimer
|
|
|
|
|
interval: 400
|
|
|
|
|
property bool deleteTriggered: false
|
|
|
|
|
onTriggered: deleteTriggered = true
|
|
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-13 16:52:29 +01:00
|
|
|
Image {
|
2023-03-10 22:16:37 +01:00
|
|
|
id: editIcon
|
|
|
|
|
width: 40
|
|
|
|
|
height: 40
|
|
|
|
|
y: 10
|
|
|
|
|
x: {
|
|
|
|
|
let additional = Math.max(0, Math.min(listItem.x * -1 - width, 16))
|
|
|
|
|
return parent.width + additional;
|
|
|
|
|
}
|
2023-06-12 15:26:31 +02:00
|
|
|
source: "./edit" + (Pay.useDarkSkin ? "-light" : "") + ".svg"
|
2023-03-13 16:52:29 +01:00
|
|
|
smooth: true
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 17:21:02 +01:00
|
|
|
// UX help
|
|
|
|
|
Row {
|
|
|
|
|
id: row
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
anchors.bottomMargin: 26
|
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
|
spacing: 6
|
|
|
|
|
visible: index <= 1
|
|
|
|
|
property bool dragToEdit: index === 0
|
|
|
|
|
Repeater {
|
|
|
|
|
model: 4
|
2023-03-13 13:33:45 +01:00
|
|
|
delegate: Flowee.ArrowPoint {
|
|
|
|
|
color: palette.highlight
|
|
|
|
|
anchors.verticalCenter: dragInstructions.verticalCenter
|
2023-03-12 17:21:02 +01:00
|
|
|
rotation: row.dragToEdit ? 180 : 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Flowee.Label {
|
|
|
|
|
id: dragInstructions
|
|
|
|
|
text: index === 0 ? qsTr("Drag to Edit") : qsTr("Drag to Delete");
|
|
|
|
|
font.italic: true
|
|
|
|
|
color: palette.highlight
|
|
|
|
|
}
|
|
|
|
|
Repeater {
|
|
|
|
|
model: 4
|
2023-03-13 13:33:45 +01:00
|
|
|
delegate: Flowee.ArrowPoint {
|
|
|
|
|
color: palette.highlight
|
|
|
|
|
anchors.verticalCenter: dragInstructions.verticalCenter
|
2023-03-12 17:21:02 +01:00
|
|
|
rotation: row.dragToEdit ? 180 : 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VisualSeparator {
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-10 22:16:37 +01:00
|
|
|
Behavior on x { NumberAnimation { duration: 100 } }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-11 13:28:29 +01:00
|
|
|
TextButton {
|
|
|
|
|
text: qsTr("Add Destination")
|
2024-12-26 18:42:46 +01:00
|
|
|
pageButton: true
|
2023-03-12 14:26:13 +01:00
|
|
|
onClicked: pushToThePile(destinationEditPage, payment.addExtraOutput());
|
2023-03-11 13:28:29 +01:00
|
|
|
}
|
2024-12-26 18:42:46 +01:00
|
|
|
/*TextButton {
|
2023-03-10 22:16:37 +01:00
|
|
|
text: qsTr("Add Detail...")
|
2024-12-26 18:42:46 +01:00
|
|
|
pageButton: true
|
2023-03-10 22:16:37 +01:00
|
|
|
onClicked: thePile.push(paymentDetailSelector);
|
2024-12-26 18:42:46 +01:00
|
|
|
}*/
|
2023-03-13 11:52:47 +01:00
|
|
|
Flowee.Button {
|
2023-06-30 22:27:27 +02:00
|
|
|
property bool walletNeedsDecryptFirst: !payment.account.isDecrypted && payment.account.needsPinToPay;
|
|
|
|
|
text: walletNeedsDecryptFirst ? qsTr("Unlock Wallet") : qsTr("Prepare Payment...")
|
2023-03-13 11:52:47 +01:00
|
|
|
enabled: payment.isValid
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
onClicked: {
|
2023-06-30 22:27:27 +02:00
|
|
|
if (walletNeedsDecryptFirst) {
|
|
|
|
|
thePile.push(unlockInPage);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 11:52:47 +01:00
|
|
|
payment.prepare();
|
|
|
|
|
if (payment.error !== "") {
|
2023-03-13 12:53:33 +01:00
|
|
|
errorDialog.visible = true;
|
2023-03-13 11:52:47 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
thePile.push(paymentInfoPage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-07-07 09:41:04 +02:00
|
|
|
Flowee.BroadcastFeedback {
|
2025-02-04 21:20:35 +01:00
|
|
|
id: broadcastPage
|
|
|
|
|
bitcoinAmount: payment.paymentAmount
|
|
|
|
|
fiatPrice: payment.fiatPrice
|
2025-02-04 22:33:08 +01:00
|
|
|
targetAddress: payment.niceAddress
|
2025-02-04 21:20:35 +01:00
|
|
|
status: payment.broadcastStatus
|
2025-03-16 22:14:20 +01:00
|
|
|
processNote: function process(note) {
|
|
|
|
|
payment.userComment = note
|
|
|
|
|
}
|
2025-02-04 21:20:35 +01:00
|
|
|
|
2023-07-07 09:41:04 +02:00
|
|
|
onCloseButtonPressed: {
|
|
|
|
|
var mainView = thePile.get(0);
|
|
|
|
|
mainView.currentIndex = 0; // go to the 'main' tab.
|
|
|
|
|
thePile.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-10 22:16:37 +01:00
|
|
|
}
|