mirror of
https://github.com/monero-project/monero-gui.git
synced 2024-11-16 15:58:11 +00:00
TxConfirmationDialog: implement multiple recipients support, layout fixes
This commit is contained in:
parent
c073867657
commit
92a2ae1a11
6 changed files with 133 additions and 103 deletions
|
@ -51,7 +51,7 @@ Rectangle {
|
|||
property alias flickable: mainFlickable
|
||||
|
||||
property Transfer transferView: Transfer {
|
||||
onPaymentClicked: root.paymentClicked(address, paymentId, amount, mixinCount, priority, description)
|
||||
onPaymentClicked: root.paymentClicked(recipients, paymentId, mixinCount, priority, description)
|
||||
onSweepUnmixableClicked: root.sweepUnmixableClicked()
|
||||
}
|
||||
property Receive receiveView: Receive { }
|
||||
|
@ -66,7 +66,7 @@ Rectangle {
|
|||
property Keys keysView: Keys { }
|
||||
property Account accountView: Account { }
|
||||
|
||||
signal paymentClicked(string address, string paymentId, string amount, int mixinCount, int priority, string description)
|
||||
signal paymentClicked(var recipients, string paymentId, int mixinCount, int priority, string description)
|
||||
signal sweepUnmixableClicked()
|
||||
signal generatePaymentIdInvoked()
|
||||
signal getProofClicked(string txid, string address, string message);
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls 1.4 as QtQuickControls1
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
@ -35,11 +36,14 @@ import FontAwesome 1.0
|
|||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property int margins: 25
|
||||
|
||||
x: parent.width/2 - root.width/2
|
||||
y: parent.height/2 - root.height/2
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 580
|
||||
height: 400
|
||||
width: 590
|
||||
height: layout.height + layout.anchors.margins * 2
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "white"
|
||||
visible: false
|
||||
radius: 10
|
||||
|
@ -53,8 +57,8 @@ Rectangle {
|
|||
}
|
||||
KeyNavigation.tab: confirmButton
|
||||
|
||||
property var recipients: []
|
||||
property var transactionAmount: ""
|
||||
property var transactionAddress: ""
|
||||
property var transactionDescription: ""
|
||||
property var transactionFee: ""
|
||||
property var transactionPriority: ""
|
||||
|
@ -127,8 +131,8 @@ Rectangle {
|
|||
}
|
||||
|
||||
function clearFields() {
|
||||
root.recipients = [];
|
||||
root.transactionAmount = "";
|
||||
root.transactionAddress = "";
|
||||
root.transactionDescription = "";
|
||||
root.transactionFee = "";
|
||||
root.transactionPriority = "";
|
||||
|
@ -141,9 +145,12 @@ Rectangle {
|
|||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: parent.margins
|
||||
spacing: 10
|
||||
anchors.fill: parent
|
||||
anchors.margins: 25
|
||||
|
||||
RowLayout {
|
||||
Layout.topMargin: 10
|
||||
|
@ -181,11 +188,11 @@ Rectangle {
|
|||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 71
|
||||
|
||||
BusyIndicator {
|
||||
id: txAmountBusyIndicator
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
running: root.transactionAmount == "(all)"
|
||||
QtQuickControls1.BusyIndicator {
|
||||
id: txAmountBusyIndicator
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
running: root.transactionAmount == "(all)"
|
||||
}
|
||||
|
||||
Text {
|
||||
|
@ -220,16 +227,10 @@ Rectangle {
|
|||
columnSpacing: 15
|
||||
rowSpacing: 16
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("From") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
Text {
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("From") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
@ -266,57 +267,70 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 15
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("To") + ":" + translationManager.emptyString
|
||||
}
|
||||
Text {
|
||||
font.pixelSize: 15
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("To") + ":" + translationManager.emptyString
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Flickable {
|
||||
id: flickable
|
||||
property int linesInMultipleRecipientsMode: 7
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
Layout.preferredHeight: recipients.length > 1
|
||||
? linesInMultipleRecipientsMode * (recipientsArea.contentHeight / recipientsArea.lineCount)
|
||||
: recipientsArea.contentHeight
|
||||
boundsBehavior: isMac ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 15
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.Wrap
|
||||
TextArea.flickable: TextArea {
|
||||
id : recipientsArea
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.family: MoneroComponents.Style.fontMonoRegular.name
|
||||
font.pixelSize: 14
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
textMargin: 0
|
||||
readOnly: true
|
||||
selectByKeyboard: true
|
||||
selectByMouse: true
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
textFormat: TextEdit.RichText
|
||||
wrapMode: TextEdit.Wrap
|
||||
text: {
|
||||
if (root.transactionAddress) {
|
||||
const addressBookName = currentWallet ? currentWallet.addressBook.getDescription(root.transactionAddress) : null;
|
||||
var fulladdress = root.transactionAddress;
|
||||
var spacedaddress = fulladdress.match(/.{1,4}/g);
|
||||
var spacedaddress = spacedaddress.join(' ');
|
||||
if (!addressBookName) {
|
||||
return qsTr("Monero address") + "<br>" + spacedaddress + translationManager.emptyString;
|
||||
} else {
|
||||
return FontAwesome.addressBook + " " + addressBookName + "<br>" + spacedaddress;
|
||||
return recipients.map(function (recipient, index) {
|
||||
var addressBookName = null;
|
||||
if (currentWallet) {
|
||||
addressBookName = currentWallet.addressBook.getDescription(recipient.address);
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
var title;
|
||||
if (addressBookName) {
|
||||
title = FontAwesome.addressBook + " " + addressBookName;
|
||||
} else {
|
||||
title = qsTr("Monero address") + translationManager.emptyString;
|
||||
}
|
||||
if (recipients.length > 1) {
|
||||
title = "%1. %2 - %3 XMR".arg(index + 1).arg(title).arg(recipient.amount);
|
||||
if (persistentSettings.fiatPriceEnabled) {
|
||||
title += " (%1)".arg(showFiatConversion(recipient.amount));
|
||||
}
|
||||
}
|
||||
const spacedaddress = recipient.address.match(/.{1,4}/g).join(' ');
|
||||
return title + "<br>" + spacedaddress;
|
||||
}).join("<br><br>");
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: recipientsArea.contentHeight > flickable.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("Fee") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
Text {
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("Fee") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
@ -364,7 +378,7 @@ Rectangle {
|
|||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 50
|
||||
|
||||
BusyIndicator {
|
||||
QtQuickControls1.BusyIndicator {
|
||||
visible: !bottomTextAnimation.running
|
||||
running: !bottomTextAnimation.running
|
||||
scale: .5
|
||||
|
|
61
main.qml
61
main.qml
|
@ -852,48 +852,49 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
function getDisplayAmountTotal(recipients) {
|
||||
const amounts = recipients.map(function (recipient) {
|
||||
return recipient.amount;
|
||||
});
|
||||
const total = walletManager.amountsSumFromStrings(amounts);
|
||||
return Utils.removeTrailingZeros(walletManager.displayAmount(total));
|
||||
}
|
||||
|
||||
// called on "transfer"
|
||||
function handlePayment(address, paymentId, amount, mixinCount, priority, description, createFile) {
|
||||
function handlePayment(recipients, paymentId, mixinCount, priority, description, createFile) {
|
||||
console.log("Creating transaction: ")
|
||||
console.log("\taddress: ", address,
|
||||
console.log("\trecipients: ", recipients,
|
||||
", payment_id: ", paymentId,
|
||||
", amount: ", amount,
|
||||
", mixins: ", mixinCount,
|
||||
", priority: ", priority,
|
||||
", description: ", description);
|
||||
txConfirmationPopup.bottomTextAnimation.running = false
|
||||
txConfirmationPopup.bottomText.text = qsTr("Creating transaction...") + translationManager.emptyString;
|
||||
txConfirmationPopup.transactionAddress = address;
|
||||
txConfirmationPopup.transactionAmount = Utils.removeTrailingZeros(amount);
|
||||
txConfirmationPopup.transactionPriority = priority;
|
||||
txConfirmationPopup.transactionDescription = description;
|
||||
|
||||
// validate amount;
|
||||
if (amount !== "(all)") {
|
||||
var amountxmr = walletManager.amountFromString(amount);
|
||||
console.log("integer amount: ", amountxmr);
|
||||
console.log("integer unlocked", currentWallet.unlockedBalance())
|
||||
if (amountxmr <= 0) {
|
||||
txConfirmationPopup.errorText.text = qsTr("Amount is wrong: expected number from %1 to %2")
|
||||
.arg(walletManager.displayAmount(0))
|
||||
.arg(walletManager.displayAmount(currentWallet.unlockedBalance()))
|
||||
+ translationManager.emptyString;
|
||||
return;
|
||||
} else if (amountxmr > currentWallet.unlockedBalance()) {
|
||||
txConfirmationPopup.errorText.text = qsTr("Insufficient funds. Unlocked balance: %1")
|
||||
.arg(walletManager.displayAmount(currentWallet.unlockedBalance()))
|
||||
+ translationManager.emptyString;
|
||||
return;
|
||||
}
|
||||
const recipientAll = recipients.find(function (recipient) {
|
||||
return recipient.amount == "(all)";
|
||||
});
|
||||
if (recipientAll && recipients.length > 1) {
|
||||
throw "Sending all requires one destination address";
|
||||
}
|
||||
|
||||
txConfirmationPopup.bottomTextAnimation.running = false;
|
||||
txConfirmationPopup.bottomText.text = qsTr("Creating transaction...") + translationManager.emptyString;
|
||||
txConfirmationPopup.recipients = recipients;
|
||||
txConfirmationPopup.transactionAmount = recipientAll ? "(all)" : getDisplayAmountTotal(recipients);
|
||||
txConfirmationPopup.transactionPriority = priority;
|
||||
txConfirmationPopup.transactionDescription = description;
|
||||
txConfirmationPopup.open();
|
||||
|
||||
if (amount === "(all)")
|
||||
currentWallet.createTransactionAllAsync(address, paymentId, mixinCount, priority);
|
||||
else
|
||||
currentWallet.createTransactionAsync([address], paymentId, [amountxmr], mixinCount, priority);
|
||||
if (recipientAll) {
|
||||
currentWallet.createTransactionAllAsync(recipientAll.address, paymentId, mixinCount, priority);
|
||||
} else {
|
||||
const addresses = recipients.map(function (recipient) {
|
||||
return recipient.address;
|
||||
});
|
||||
const amountsxmr = recipients.map(function (recipient) {
|
||||
return walletManager.amountFromString(recipient.amount);
|
||||
});
|
||||
currentWallet.createTransactionAsync(addresses, paymentId, amountsxmr, mixinCount, priority);
|
||||
}
|
||||
}
|
||||
|
||||
//Choose where to save transaction
|
||||
|
|
|
@ -44,8 +44,7 @@ import "../js/Utils.js" as Utils
|
|||
|
||||
Rectangle {
|
||||
id: root
|
||||
signal paymentClicked(string address, string paymentId, string amount, int mixinCount,
|
||||
int priority, string description)
|
||||
signal paymentClicked(var recipients, string paymentId, int mixinCount, int priority, string description)
|
||||
signal sweepUnmixableClicked()
|
||||
|
||||
color: "transparent"
|
||||
|
@ -124,6 +123,10 @@ Rectangle {
|
|||
priorityDropdown.currentIndex = 0
|
||||
}
|
||||
|
||||
function getRecipients() {
|
||||
return [{address: addressLine.text, amount: amountLine.text}];
|
||||
}
|
||||
|
||||
// Information dialog
|
||||
StandardDialog {
|
||||
// dynamically change onclose handler
|
||||
|
@ -523,7 +526,7 @@ Rectangle {
|
|||
console.log("amount: " + amountLine.text)
|
||||
addressLine.text = addressLine.text.trim()
|
||||
setPaymentId(paymentIdLine.text.trim());
|
||||
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
|
||||
root.paymentClicked(getRecipients(), paymentIdLine.text, root.mixin, priority, descriptionLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -597,7 +600,7 @@ Rectangle {
|
|||
console.log("amount: " + amountLine.text)
|
||||
addressLine.text = addressLine.text.trim()
|
||||
setPaymentId(paymentIdLine.text.trim());
|
||||
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
|
||||
root.paymentClicked(getRecipients(), paymentIdLine.text, root.mixin, priority, descriptionLine.text)
|
||||
}
|
||||
button2.text: qsTr("Sign (offline)") + translationManager.emptyString
|
||||
button2.enabled: !appWindow.viewOnly
|
||||
|
|
|
@ -251,7 +251,7 @@ QString WalletManager::errorString() const
|
|||
return tr("Unknown error");
|
||||
}
|
||||
|
||||
quint64 WalletManager::maximumAllowedAmount() const
|
||||
quint64 WalletManager::maximumAllowedAmount()
|
||||
{
|
||||
return Monero::Wallet::maximumAllowedAmount();
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ QString WalletManager::displayAmount(quint64 amount)
|
|||
return QString::fromStdString(Monero::Wallet::displayAmount(amount));
|
||||
}
|
||||
|
||||
quint64 WalletManager::amountFromString(const QString &amount) const
|
||||
quint64 WalletManager::amountFromString(const QString &amount)
|
||||
{
|
||||
return Monero::Wallet::amountFromString(amount.toStdString());
|
||||
}
|
||||
|
@ -276,6 +276,17 @@ quint64 WalletManager::amountFromDouble(double amount) const
|
|||
return Monero::Wallet::amountFromDouble(amount);
|
||||
}
|
||||
|
||||
QString WalletManager::amountsSumFromStrings(const QVector<QString> &amounts)
|
||||
{
|
||||
quint64 sum = 0;
|
||||
for (const auto &amountString : amounts)
|
||||
{
|
||||
const quint64 amount = amountFromString(amountString);
|
||||
sum = sum + std::min(maximumAllowedAmount() - sum, amount);
|
||||
}
|
||||
return QString::number(sum);
|
||||
}
|
||||
|
||||
bool WalletManager::paymentIdValid(const QString &payment_id) const
|
||||
{
|
||||
return Monero::Wallet::paymentIdValid(payment_id.toStdString());
|
||||
|
|
|
@ -133,9 +133,10 @@ public:
|
|||
|
||||
//! since we can't call static method from QML, move it to this class
|
||||
Q_INVOKABLE static QString displayAmount(quint64 amount);
|
||||
Q_INVOKABLE quint64 amountFromString(const QString &amount) const;
|
||||
Q_INVOKABLE static quint64 amountFromString(const QString &amount);
|
||||
Q_INVOKABLE quint64 amountFromDouble(double amount) const;
|
||||
Q_INVOKABLE quint64 maximumAllowedAmount() const;
|
||||
Q_INVOKABLE static QString amountsSumFromStrings(const QVector<QString> &amounts);
|
||||
Q_INVOKABLE static quint64 maximumAllowedAmount();
|
||||
|
||||
// QML JS engine doesn't support unsigned integers
|
||||
Q_INVOKABLE QString maximumAllowedAmountAsString() const;
|
||||
|
|
Loading…
Reference in a new issue