From af4e49cd0f4b871ae15a1793232e3a81d85e3126 Mon Sep 17 00:00:00 2001 From: "moneromooo.monero" Date: Mon, 21 Nov 2016 14:58:53 +0000 Subject: [PATCH 1/3] MiddlePanel: add an optional onPageClosed callback to match onPageCompleted --- MiddlePanel.qml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MiddlePanel.qml b/MiddlePanel.qml index e14255a5..c0bcf164 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -39,6 +39,7 @@ Rectangle { id: root property Item currentView + property Item previousView property bool basicMode : false property string balanceText property string unlockedBalanceText @@ -62,6 +63,12 @@ Rectangle { color: "#F0EEEE" onCurrentViewChanged: { + if (previousView) { + if (typeof previousView.onPageClosed === "function") { + previousView.onPageClosed(); + } + } + previousView = currentView if (currentView) { stackView.replace(currentView) From 87d07515a8570caaad0eeda7e74152e67ab7db45 Mon Sep 17 00:00:00 2001 From: "moneromooo.monero" Date: Thu, 24 Nov 2016 11:14:46 +0000 Subject: [PATCH 2/3] libwalletqt: add a few daemon read access routines --- main.qml | 2 ++ src/libwalletqt/WalletManager.cpp | 30 ++++++++++++++++++++++++++++++ src/libwalletqt/WalletManager.h | 8 ++++++++ 3 files changed, 40 insertions(+) diff --git a/main.qml b/main.qml index 5f28e415..3f4e4aea 100644 --- a/main.qml +++ b/main.qml @@ -176,6 +176,8 @@ ApplicationWindow { } + walletManager.setDaemonAddress(persistentSettings.daemon_address) + // wallet already opened with wizard, we just need to initialize it if (typeof wizard.settings['wallet'] !== 'undefined') { console.log("using wizard wallet") diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index de800776..a5f4818a 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -174,6 +174,36 @@ QString WalletManager::checkPayment(const QString &address, const QString &txid, return QString::fromStdString(result); } +void WalletManager::setDaemonAddress(const QString &address) +{ + m_pimpl->setDaemonAddress(address.toStdString()); +} + +bool WalletManager::connected() const +{ + return m_pimpl->connected(); +} + +quint64 WalletManager::networkDifficulty() const +{ + return m_pimpl->networkDifficulty(); +} + +quint64 WalletManager::blockchainHeight() const +{ + return m_pimpl->blockchainHeight(); +} + +quint64 WalletManager::blockchainTargetHeight() const +{ + return m_pimpl->blockchainTargetHeight(); +} + +double WalletManager::miningHashRate() const +{ + return m_pimpl->miningHashRate(); +} + void WalletManager::setLogLevel(int logLevel) { Bitmonero::WalletManagerFactory::setLogLevel(logLevel); diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index 8e63050a..687008e9 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -13,6 +13,7 @@ namespace Bitmonero { class WalletManager : public QObject { Q_OBJECT + Q_PROPERTY(bool connected READ connected) public: enum LogLevel { @@ -95,6 +96,13 @@ public: Q_INVOKABLE QString checkPayment(const QString &address, const QString &txid, const QString &txkey, const QString &daemon_address) const; + Q_INVOKABLE void setDaemonAddress(const QString &address); + Q_INVOKABLE bool connected() const; + Q_INVOKABLE quint64 networkDifficulty() const; + Q_INVOKABLE quint64 blockchainHeight() const; + Q_INVOKABLE quint64 blockchainTargetHeight() const; + Q_INVOKABLE double miningHashRate() const; + // QML missing such functionality, implementing these helpers here Q_INVOKABLE QString urlToLocalPath(const QUrl &url) const; Q_INVOKABLE QUrl localPathToUrl(const QString &path) const; From ab619109e2213dc72a771a106580e63c01b953ff Mon Sep 17 00:00:00 2001 From: "moneromooo.monero" Date: Sun, 27 Nov 2016 10:57:13 +0000 Subject: [PATCH 3/3] Receive: tx scanner for payments matching setup Payments matching the currently setup payment id and optional amount are detected, allowing a merchant to generate a new payment id, enter the expected amount, let a client scan the QR code, and wait till the payment shows up as either in the txpool, or with N confirmations. --- pages/Receive.qml | 143 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/pages/Receive.qml b/pages/Receive.qml index ce3d2320..3787033e 100644 --- a/pages/Receive.qml +++ b/pages/Receive.qml @@ -30,9 +30,14 @@ import QtQuick 2.0 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 import "../components" 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 Rectangle { @@ -40,6 +45,8 @@ Rectangle { property alias addressText : addressLine.text property alias paymentIdText : paymentIdLine.text property alias integratedAddressText : integratedAddressLine.text + property var model + property string trackingLineText: "" function updatePaymentId(payment_id) { if (typeof appWindow.currentWallet === 'undefined' || appWindow.currentWallet == null) @@ -54,6 +61,7 @@ Rectangle { integratedAddressLine.text = appWindow.currentWallet.integratedAddress(payment_id) if (integratedAddressLine.text === "") integratedAddressLine.text = qsTr("Invalid payment ID") + update() } function makeQRCodeString() { @@ -73,6 +81,80 @@ Rectangle { return s } + function setTrackingLineText(text) { + // don't replace with same text, it wrecks selection while the user is selecting + // also keep track of text, because when we read back the text from the widget, + // we do not get what we put it, but some extra HTML stuff on top + if (text != trackingLineText) { + trackingLine.text = text + trackingLineText = text + } + } + + function update() { + if (!appWindow.currentWallet) { + setTrackingLineText("-") + return + } + if (appWindow.currentWallet.connected == Wallet.ConnectionStatus_Disconnected) { + setTrackingLineText(qsTr("WARNING: no connection to daemon")) + return + } + + var model = appWindow.currentWallet.historyModel + var count = model.rowCount() + var totalAmount = 0 + var nTransactions = 0 + var list = "" + var blockchainHeight = 0 + for (var i = 0; i < count; ++i) { + var idx = model.index(i, 0) + var isout = model.data(idx, TransactionHistoryModel.TransactionIsOutRole); + var payment_id = model.data(idx, TransactionHistoryModel.TransactionPaymentIdRole); + if (!isout && payment_id == paymentIdLine.text) { + var amount = model.data(idx, TransactionHistoryModel.TransactionAtomicAmountRole); + totalAmount = walletManager.addi(totalAmount, amount) + nTransactions += 1 + + var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole); + var blockHeight = model.data(idx, TransactionHistoryModel.TransactionBlockHeightRole); + if (blockHeight == 0) { + list += qsTr("in the txpool: %1").arg(txid) + translationManager.emptyString + } else { + if (blockchainHeight == 0) + blockchainHeight = walletManager.blockchainHeight() + var confirmations = blockchainHeight - blockHeight - 1 + var displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole); + if (confirmations > 1) { + list += qsTr("%2 confirmations: %3 (%1)").arg(txid).arg(confirmations).arg(displayAmount) + translationManager.emptyString + } else { + list += qsTr("1 confirmation: %2 (%1)").arg(txid).arg(displayAmount) + translationManager.emptyString + } + } + list += "
" + } + } + + if (nTransactions == 0) { + setTrackingLineText(qsTr("No transaction found yet...") + translationManager.emptyString) + return + } + + var text = ((nTransactions == 1) ? qsTr("Transaction found") : qsTr("%1 transactions found").arg(nTransactions)) + translationManager.emptyString + + var expectedAmount = walletManager.amountFromString(amountLine.text) + if (expectedAmount && expectedAmount != amount) { + var displayTotalAmount = walletManager.displayAmount(totalAmount) + if (amount > expectedAmount) { + text += qsTr(" with more money (%1)").arg(displayTotalAmount) + translationManager.emptyString + } else if (amount < expectedAmount) { + text += qsTr(" with not enough money (%1)").arg(displayTotalAmount) + translationManager.emptyString + } + } + + setTrackingLineText(text + "
" + list) + } + Clipboard { id: clipboard } @@ -228,10 +310,58 @@ Rectangle { } } + RowLayout { + id: trackingRow + + Label { + id: trackingLabel + fontSize: 14 + textFormat: Text.RichText + text: qsTr("\ + Tracking (help)") + + translationManager.emptyString + width: mainLayout.labelWidth + onLinkActivated: { + trackingHowToUseDialog.title = qsTr("Tracking payments") + translationManager.emptyString; + trackingHowToUseDialog.text = qsTr( + "

This is a simple sales tracker:

" + + "

Click Generate to create a random payment id for a new customer

" + + "

Let your customer scan that QR code to make a payment (if that customer has software which " + + "supports QR code scanning).

" + + "

This page will automatically scan the blockchain and the tx pool " + + "for incoming transactions using this QR code. If you input an amount, it will also check " + + "that incoming transactions total up to that amount.

" + + "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).

" + ) + trackingHowToUseDialog.icon = StandardIcon.Information + trackingHowToUseDialog.open() + } + } + + TextEdit { + id: trackingLine + anchors.top: trackingRow.top + textFormat: Text.RichText + text: "" + readOnly: true + width: mainLayout.editWidth + Layout.fillWidth: true + selectByMouse: true + } + + } + + MessageDialog { + id: trackingHowToUseDialog + standardButtons: StandardButton.Ok + } + Image { id: qrCode anchors.margins: 50 - anchors.top: amountRow.bottom + anchors.top: trackingRow.bottom Layout.fillWidth: true Layout.minimumHeight: mainLayout.qrCodeSize smooth: false @@ -240,13 +370,24 @@ Rectangle { } } + Timer { + id: timer + interval: 2000; running: false; repeat: true + onTriggered: update() + } + function onPageCompleted() { console.log("Receive page loaded"); if(addressLine.text.length === 0 || addressLine.text !== appWindow.currentWallet.address) { updatePaymentId() } + update() + timer.running = true } + function onPageClosed() { + timer.running = false + } }