From a344f17cd1716c34672e2c201137d772b165da2e Mon Sep 17 00:00:00 2001 From: Jaquee Date: Sat, 10 Dec 2016 02:01:04 +0100 Subject: [PATCH] AddressBook: basic functions --- LeftPanel.qml | 13 ++--- MiddlePanel.qml | 9 +++- components/AddressBookTable.qml | 25 +++++++--- components/TableDropdown.qml | 5 ++ main.cpp | 11 +++- monero-core.pro | 8 ++- pages/AddressBook.qml | 58 +++++++++++++++++++-- pages/Transfer.qml | 16 +++--- src/libwalletqt/AddressBook.cpp | 70 ++++++++++++++++++++++++++ src/libwalletqt/AddressBook.h | 50 +++++++++++++++++++ src/libwalletqt/PendingTransaction.h | 3 +- src/libwalletqt/Wallet.cpp | 27 +++++++++- src/libwalletqt/Wallet.h | 16 +++++- src/model/AddressBookModel.cpp | 75 ++++++++++++++++++++++++++++ src/model/AddressBookModel.h | 38 ++++++++++++++ 15 files changed, 387 insertions(+), 37 deletions(-) create mode 100644 src/libwalletqt/AddressBook.cpp create mode 100644 src/libwalletqt/AddressBook.h create mode 100644 src/model/AddressBookModel.cpp create mode 100644 src/model/AddressBookModel.h diff --git a/LeftPanel.qml b/LeftPanel.qml index b1bb1d49..cd87de7e 100644 --- a/LeftPanel.qml +++ b/LeftPanel.qml @@ -311,14 +311,7 @@ Rectangle { color: "#505050" height: 1 } - /* - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 16 - color: historyButton.checked || addressBookButton.checked ? "#1C1C1C" : "#505050" - height: 1 - } + // ------------- AddressBook tab --------------- MenuButton { @@ -339,11 +332,11 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.leftMargin: 16 - color: addressBookButton.checked || miningButton.checked ? "#1C1C1C" : "#505050" + color: "#505050" height: 1 } - // ------------- Mining tab --------------- + /* // ------------- Mining tab --------------- MenuButton { id: miningButton anchors.left: parent.left diff --git a/MiddlePanel.qml b/MiddlePanel.qml index 60f79185..6f6e3043 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -51,6 +51,7 @@ Rectangle { property History historyView: History { } property Sign signView: Sign { } property Settings settingsView: Settings { } + property AddressBook addressBookView: AddressBook { } signal paymentClicked(string address, string paymentId, string amount, int mixinCount, int priority, string description) @@ -81,6 +82,12 @@ Rectangle { transferView.updateStatus(); } + // send from AddressBook + function sendTo(address, paymentId, description){ + root.state = "Transfer"; + transferView.sendTo(address, paymentId, description); + } + // XXX: just for memo, to be removed // states: [ @@ -127,7 +134,7 @@ Rectangle { PropertyChanges { target: root; currentView: txkeyView } }, State { name: "AddressBook" - PropertyChanges { /*TODO*/ } + PropertyChanges { target: root; currentView: addressBookView } }, State { name: "Sign" PropertyChanges { target: root; currentView: signView } diff --git a/components/AddressBookTable.qml b/components/AddressBookTable.qml index 2ad9d2ff..129b932c 100644 --- a/components/AddressBookTable.qml +++ b/components/AddressBookTable.qml @@ -77,14 +77,14 @@ ListView { } } - Text { + TextEdit { id: addressText + selectByMouse: true anchors.bottom: descriptionText.bottom anchors.left: descriptionText.right anchors.right: dropdown.left anchors.leftMargin: description.length > 0 ? 12 : 0 - anchors.rightMargin: 12 - elide: Text.ElideRight + anchors.rightMargin: 40 font.family: "Arial" font.pixelSize: 16 font.letterSpacing: -1 @@ -103,17 +103,18 @@ ListView { font.pixelSize: 12 font.letterSpacing: -1 color: "#535353" - text: qsTr("Payment ID:") + + translationManager.emptyString + text: qsTr("Payment ID:") + translationManager.emptyString } - Text { + TextEdit { + selectByMouse: true; anchors.bottom: paymentLabel.bottom anchors.left: paymentLabel.right anchors.leftMargin: 12 anchors.rightMargin: 12 anchors.right: dropdown.left - elide: Text.ElideRight + font.family: "Arial" font.pixelSize: 13 font.letterSpacing: -1 @@ -125,7 +126,7 @@ ListView { id: dropModel ListElement { name: "Copy address to clipboard"; icon: "../images/dropdownCopy.png" } ListElement { name: "Send to same destination"; icon: "../images/dropdownSend.png" } - ListElement { name: "Find similar transactions"; icon: "../images/dropdownSearch.png" } +// ListElement { name: "Find similar transactions"; icon: "../images/dropdownSearch.png" } ListElement { name: "Remove from history"; icon: "../images/dropdownDel.png" } } @@ -146,6 +147,16 @@ ListView { onOptionClicked: { if(option === 0) clipboard.setText(address) + else if(option === 1){ + console.log("Sending to: ", address +" "+ paymentId); + middlePanel.sendTo(address, paymentId, description); + leftPanel.selectItem(middlePanel.state) + } else if(option === 2){ + console.log("Delete: ", rowId); + currentWallet.addressBookModel.deleteRow(rowId); + } + + } } diff --git a/components/TableDropdown.qml b/components/TableDropdown.qml index 2aa2d04c..7e5ea78e 100644 --- a/components/TableDropdown.qml +++ b/components/TableDropdown.qml @@ -148,6 +148,10 @@ Item { } } + onClicked: { + optionClicked(currentIndex) + } + onExited: timer.start() preventStealing: true z: 1 @@ -216,6 +220,7 @@ Item { tipItem.visible = true } } + } } } diff --git a/main.cpp b/main.cpp index 644c6270..9f87124f 100644 --- a/main.cpp +++ b/main.cpp @@ -46,6 +46,8 @@ #include "model/TransactionHistoryModel.h" #include "model/TransactionHistorySortFilterModel.h" #include "daemon/DaemonManager.h" +#include "AddressBook.h" +#include "model/AddressBookModel.h" int main(int argc, char *argv[]) @@ -92,6 +94,13 @@ int main(int argc, char *argv[]) qmlRegisterUncreatableType("moneroComponents.DaemonManager", 1, 0, "DaemonManager", "DaemonManager can't be instantiated directly"); + + qmlRegisterUncreatableType("moneroComponents.AddressBookModel", 1, 0, "AddressBookModel", + "AddressBookModel can't be instantiated directly"); + + qmlRegisterUncreatableType("moneroComponents.AddressBook", 1, 0, "AddressBook", + "AddressBook can't be instantiated directly"); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -152,7 +161,7 @@ int main(int argc, char *argv[]) QObject::connect(eventFilter, SIGNAL(mousePressed(QVariant,QVariant,QVariant)), rootObject, SLOT(mousePressed(QVariant,QVariant,QVariant))); QObject::connect(eventFilter, SIGNAL(mouseReleased(QVariant,QVariant,QVariant)), rootObject, SLOT(mouseReleased(QVariant,QVariant,QVariant))); - // WalletManager::instance()->setLogLevel(WalletManager::LogLevel_Max); + //WalletManager::instance()->setLogLevel(WalletManager::LogLevel_Max); return app.exec(); } diff --git a/monero-core.pro b/monero-core.pro index 7693d6e2..60fecd1f 100644 --- a/monero-core.pro +++ b/monero-core.pro @@ -32,7 +32,9 @@ HEADERS += \ src/QR-Code-generator/BitBuffer.hpp \ src/QR-Code-generator/QrCode.hpp \ src/QR-Code-generator/QrSegment.hpp \ - src/daemon/DaemonManager.h + src/daemon/DaemonManager.h \ + src/model/AddressBookModel.h \ + src/libwalletqt/AddressBook.h SOURCES += main.cpp \ @@ -52,7 +54,9 @@ SOURCES += main.cpp \ src/QR-Code-generator/BitBuffer.cpp \ src/QR-Code-generator/QrCode.cpp \ src/QR-Code-generator/QrSegment.cpp \ - src/daemon/DaemonManager.cpp + src/daemon/DaemonManager.cpp \ + src/model/AddressBookModel.cpp \ + src/libwalletqt/AddressBook.cpp lupdate_only { SOURCES = *.qml \ diff --git a/pages/AddressBook.qml b/pages/AddressBook.qml index e38a938b..8d8be0a7 100644 --- a/pages/AddressBook.qml +++ b/pages/AddressBook.qml @@ -28,9 +28,13 @@ import QtQuick 2.0 import "../components" +import moneroComponents.AddressBook 1.0 +import moneroComponents.AddressBookModel 1.0 Rectangle { color: "#F0EEEE" + id: root + property var model Text { id: newEntryText @@ -66,6 +70,7 @@ Rectangle { anchors.leftMargin: 17 anchors.rightMargin: 17 anchors.topMargin: 5 + error: true; } Label { @@ -124,6 +129,27 @@ Rectangle { releasedColor: "#FF6C3C" pressedColor: "#FF4304" text: qsTr("ADD") + enabled: checkInformation(addressLine.text, paymentIdLine.text, appWindow.persistentSettings.testnet) + + onClicked: { + if (!currentWallet.addressBook.addRow(addressLine.text.trim(), paymentIdLine.text.trim(), descriptionLine.text)) { + informationPopup.title = qsTr("Error") + translationManager.emptyString; + // TODO: check currentWallet.addressBook.errorString() instead. + if(currentWallet.addressBook.errorCode() === AddressBook.Invalid_Address) + informationPopup.text = qsTr("Invalid address") + else if(currentWallet.addressBook.errorCode() === AddressBook.Invalid_Payment_Id) + informationPopup.text = qsTr("Invalid Payment ID") + else + informationPopup.text = qsTr("Can't create entry") + + informationPopup.onCloseCallback = null + informationPopup.open(); + } else { + addressLine.text = ""; + paymentIdLine.text = ""; + descriptionLine.text = ""; + } + } } Item { @@ -170,9 +196,10 @@ Rectangle { ListModel { id: columnsModel - ListElement { columnName: qsTr("Address") + translationManager.emptyString; columnWidth: 148 } - ListElement { columnName: qsTr("Payment ID") + translationManager.emptyString; columnWidth: 148 } - ListElement { columnName: qsTr("Description") + translationManager.emptyString; columnWidth: 148 } +// ListElement { columnName: qsTr("Address") + translationManager.emptyString; columnWidth: 148 } +// ListElement { columnName: qsTr("Payment ID") + translationManager.emptyString; columnWidth: 148 } +// ListElement { columnName: qsTr("Description") + translationManager.emptyString; columnWidth: 148 } +// } TableHeader { @@ -223,7 +250,30 @@ Rectangle { anchors.leftMargin: 14 anchors.rightMargin: 14 onContentYChanged: flickableScroll.flickableContentYChanged() - model: testModel + model: root.model } } + + function checkInformation(address, payment_id, testnet) { + address = address.trim() + payment_id = payment_id.trim() + + var address_ok = walletManager.addressValid(address, testnet) + var payment_id_ok = payment_id.length == 0 || walletManager.paymentIdValid(payment_id) + var ipid = walletManager.paymentIdFromAddress(address, testnet) + if (ipid.length > 0 && payment_id.length > 0) + payment_id_ok = false + + addressLine.error = !address_ok + paymentIdLine.error = !payment_id_ok + + return address_ok && payment_id_ok + } + + function onPageCompleted() { + console.log("adress book"); + root.model = currentWallet.addressBookModel; + } + + } diff --git a/pages/Transfer.qml b/pages/Transfer.qml index c19c8594..649750bb 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -264,15 +264,6 @@ Rectangle { anchors.topMargin: 5 } - function checkAddressAndPaymentID(address, payment_id, testnet) { - if (!walletManager.addressValid(address, testnet)) - return false - var ipid = walletManager.paymentIdFromAddress(address, testnet) - if (ipid.length > 0) - return payment_id === "" - return payment_id === "" || walletManager.paymentIdValid(payment_id) - } - function checkInformation(amount, address, payment_id, testnet) { address = address.trim() payment_id = payment_id.trim() @@ -404,4 +395,11 @@ Rectangle { } } + + // Popuplate fields from addressbook. + function sendTo(address, paymentId, description){ + addressLine.text = address + paymentIdLine.text = paymentId + descriptionLine.text = description + } } diff --git a/src/libwalletqt/AddressBook.cpp b/src/libwalletqt/AddressBook.cpp new file mode 100644 index 00000000..971fa17b --- /dev/null +++ b/src/libwalletqt/AddressBook.cpp @@ -0,0 +1,70 @@ +#include "AddressBook.h" +#include + +AddressBook::AddressBook(Bitmonero::AddressBook *abImpl,QObject *parent) + : QObject(parent), m_addressBookImpl(abImpl) +{ + qDebug(__FUNCTION__); + getAll(); +} + +QString AddressBook::errorString() const +{ + return QString::fromStdString(m_addressBookImpl->errorString()); +} + +int AddressBook::errorCode() const +{ + return m_addressBookImpl->errorCode(); +} + +QList AddressBook::getAll(bool update) const +{ + qDebug(__FUNCTION__); + + emit refreshStarted(); + + if(update) + m_rows.clear(); + + if (m_rows.empty()){ + for (auto &abr: m_addressBookImpl->getAll()) { + m_rows.append(abr); + } + } + + emit refreshFinished(); + return m_rows; + +} + +Bitmonero::AddressBookRow * AddressBook::getRow(int index) const +{ + return m_rows.at(index); +} + +bool AddressBook::addRow(const QString &address, const QString &payment_id, const QString &description) const +{ + // virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0; + bool r = m_addressBookImpl->addRow(address.toStdString(), payment_id.toStdString(), description.toStdString()); + + if(r) + getAll(true); + + return r; +} + +bool AddressBook::deleteRow(int rowId) const +{ + bool r = m_addressBookImpl->deleteRow(rowId); + + // Fetch new data from wallet2. + getAll(true); + + return r; +} + +quint64 AddressBook::count() const +{ + return m_rows.size(); +} diff --git a/src/libwalletqt/AddressBook.h b/src/libwalletqt/AddressBook.h new file mode 100644 index 00000000..2262e00f --- /dev/null +++ b/src/libwalletqt/AddressBook.h @@ -0,0 +1,50 @@ +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include +#include +#include +#include + +namespace Bitmonero { +class AddressBook; +} +class AddressBookRow; + +class AddressBook : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE QList getAll(bool update = false) const; + Q_INVOKABLE Bitmonero::AddressBookRow * getRow(int index) const; + Q_INVOKABLE bool addRow(const QString &address, const QString &payment_id, const QString &description) const; + Q_INVOKABLE bool deleteRow(int rowId) const; + quint64 count() const; + Q_INVOKABLE QString errorString() const; + Q_INVOKABLE int errorCode() const; + + enum ErrorCode { + Status_Ok, + General_Error, + Invalid_Address, + Invalid_Payment_Id + }; + + Q_ENUM(ErrorCode); + + +signals: + void refreshStarted() const; + void refreshFinished() const; + + +public slots: + +private: + explicit AddressBook(Bitmonero::AddressBook * abImpl, QObject *parent); + friend class Wallet; + Bitmonero::AddressBook * m_addressBookImpl; + mutable QList m_rows; +}; + +#endif // ADDRESSBOOK_H diff --git a/src/libwalletqt/PendingTransaction.h b/src/libwalletqt/PendingTransaction.h index f1c7f0c6..ad2cb275 100644 --- a/src/libwalletqt/PendingTransaction.h +++ b/src/libwalletqt/PendingTransaction.h @@ -23,7 +23,8 @@ class PendingTransaction : public QObject public: enum Status { Status_Ok = Monero::PendingTransaction::Status_Ok, - Status_Error = Monero::PendingTransaction::Status_Error + Status_Error = Monero::PendingTransaction::Status_Error, + Status_Critical = Monero::PendingTransaction::Status_Critical }; Q_ENUM(Status) diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 64c7af2c..61a2920c 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -1,8 +1,10 @@ #include "Wallet.h" #include "PendingTransaction.h" #include "TransactionHistory.h" +#include "AddressBook.h" #include "model/TransactionHistoryModel.h" #include "model/TransactionHistorySortFilterModel.h" +#include "model/AddressBookModel.h" #include "wallet/wallet2_api.h" #include @@ -11,6 +13,8 @@ #include #include #include +#include +#include namespace { static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 10; @@ -48,7 +52,6 @@ public: virtual void updated() { - qDebug() << __FUNCTION__; emit m_wallet->updated(); } @@ -324,6 +327,22 @@ TransactionHistorySortFilterModel *Wallet::historyModel() const return m_historySortFilterModel; } +AddressBook *Wallet::addressBook() const +{ + return m_addressBook; +} + +AddressBookModel *Wallet::addressBookModel() const +{ + + if (!m_addressBookModel) { + Wallet * w = const_cast(this); + m_addressBookModel = new AddressBookModel(w,m_addressBook); + } + + return m_addressBookModel; +} + QString Wallet::generatePaymentId() const { @@ -437,6 +456,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) , m_walletImpl(w) , m_history(nullptr) , m_historyModel(nullptr) + , m_addressBook(nullptr) + , m_addressBookModel(nullptr) , m_daemonBlockChainHeight(0) , m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS) , m_daemonBlockChainTargetHeight(0) @@ -444,6 +465,7 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) , m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS) { m_history = new TransactionHistory(m_walletImpl->history(), this); + m_addressBook = new AddressBook(m_walletImpl->addressBook(), this); m_walletImpl->setListener(new WalletListenerImpl(this)); m_connectionStatus = Wallet::ConnectionStatus_Disconnected; // start cache timers @@ -456,6 +478,9 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) Wallet::~Wallet() { qDebug("~Wallet: Closing wallet"); + delete m_history; + Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl); + } diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index c5a98be2..24bdc15b 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -15,6 +15,8 @@ namespace Monero { class TransactionHistory; class TransactionHistoryModel; class TransactionHistorySortFilterModel; +class AddressBook; +class AddressBookModel; class Wallet : public QObject { @@ -32,13 +34,17 @@ class Wallet : public QObject Q_PROPERTY(QString paymentId READ paymentId WRITE setPaymentId) Q_PROPERTY(TransactionHistorySortFilterModel * historyModel READ historyModel NOTIFY historyModelChanged) Q_PROPERTY(QString path READ path) + Q_PROPERTY(AddressBookModel * addressBookModel READ addressBookModel) + Q_PROPERTY(AddressBook * addressBook READ addressBook) + public: enum Status { Status_Ok = Monero::Wallet::Status_Ok, - Status_Error = Monero::Wallet::Status_Error + Status_Error = Monero::Wallet::Status_Error, + Status_Critical = Monero::Wallet::Status_Critical }; Q_ENUM(Status) @@ -159,6 +165,12 @@ public: //! returns transaction history model TransactionHistorySortFilterModel *historyModel() const; + //! returns Address book + AddressBook *addressBook() const; + + //! returns adress book model + AddressBookModel *addressBookModel() const; + //! generate payment id Q_INVOKABLE QString generatePaymentId() const; @@ -227,6 +239,8 @@ private: int m_connectionStatusTtl; mutable QTime m_connectionStatusTime; mutable bool m_initialized; + AddressBook * m_addressBook; + mutable AddressBookModel * m_addressBookModel; }; diff --git a/src/model/AddressBookModel.cpp b/src/model/AddressBookModel.cpp new file mode 100644 index 00000000..5e78200b --- /dev/null +++ b/src/model/AddressBookModel.cpp @@ -0,0 +1,75 @@ +#include "AddressBookModel.h" +#include "AddressBook.h" +#include +#include +#include + +AddressBookModel::AddressBookModel(QObject *parent, AddressBook *addressBook) + : QAbstractListModel(parent) , m_addressBook(addressBook) +{ + qDebug(__FUNCTION__); + connect(m_addressBook,SIGNAL(refreshStarted()),this,SLOT(startReset())); + connect(m_addressBook,SIGNAL(refreshFinished()),this,SLOT(endReset())); + +} + +void AddressBookModel::startReset(){ + qDebug(__FUNCTION__); + beginResetModel(); +} +void AddressBookModel::endReset(){ + qDebug(__FUNCTION__); + endResetModel(); +} + +int AddressBookModel::rowCount(const QModelIndex &parent) const +{ + return m_addressBook->count(); +} + +QVariant AddressBookModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() < 0 || (unsigned)index.row() >= m_addressBook->count()) { + return QVariant(); + } + + Bitmonero::AddressBookRow * ar = m_addressBook->getRow(index.row()); + + QVariant result = ""; + switch (role) { + case AddressBookAddressRole: + result = QString::fromStdString(ar->getAddress()); + break; + case AddressBookDescriptionRole: + result = QString::fromStdString(ar->getDescription()); + break; + case AddressBookPaymentIdRole: + result = QString::fromStdString(ar->getPaymentId()); + break; + case AddressBookRowIdRole: + result = ar->getRowId(); + break; + } + + return result; +} + +bool AddressBookModel::deleteRow(int row) +{ + m_addressBook->deleteRow(row); +} + +QHash AddressBookModel::roleNames() const +{ + QHash roleNames = QAbstractListModel::roleNames(); + roleNames.insert(AddressBookAddressRole, "address"); + roleNames.insert(AddressBookPaymentIdRole, "paymentId"); + roleNames.insert(AddressBookDescriptionRole, "description"); + roleNames.insert(AddressBookRowIdRole, "rowId"); + + + return roleNames; +} diff --git a/src/model/AddressBookModel.h b/src/model/AddressBookModel.h new file mode 100644 index 00000000..d6fd01e8 --- /dev/null +++ b/src/model/AddressBookModel.h @@ -0,0 +1,38 @@ +#ifndef ADDRESSBOOKMODEL_H +#define ADDRESSBOOKMODEL_H + +#include +//#include "wallet/wallet2_api.h" // we need to have an access to the Bitmonero::Wallet::AddressBook + +class AddressBook; + +class AddressBookModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum AddressBookRowRole { + AddressBookRole = Qt::UserRole + 1, // for the AddressBookRow object; + AddressBookAddressRole, + AddressBookDescriptionRole, + AddressBookPaymentIdRole, + AddressBookRowIdRole, + }; + Q_ENUM(AddressBookRowRole) + + AddressBookModel(QObject *parent, AddressBook * addressBook); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + Q_INVOKABLE bool deleteRow(int row); + virtual QHash roleNames() const override; + +public slots: + void startReset(); + void endReset(); + +private: + AddressBook * m_addressBook; +}; + +#endif // ADDRESSBOOKMODEL_H