refactor: consolidate wallet_api into libwalletqt [1/?]

This commit is contained in:
tobtoht 2023-11-17 14:05:31 +01:00
parent a13ca973cc
commit 769af188e2
No known key found for this signature in database
GPG key ID: E45B10DD027D2472
46 changed files with 928 additions and 687 deletions

View file

@ -299,7 +299,7 @@ void CoinsWidget::freezeCoins(QStringList &pubkeys) {
for (auto &pubkey : pubkeys) {
m_wallet->coins()->freeze(pubkey);
}
m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount());
m_wallet->coins()->refresh();
m_wallet->updateBalance();
}
@ -307,7 +307,7 @@ void CoinsWidget::thawCoins(QStringList &pubkeys) {
for (auto &pubkey : pubkeys) {
m_wallet->coins()->thaw(pubkey);
}
m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount());
m_wallet->coins()->refresh();
m_wallet->updateBalance();
}

View file

@ -135,9 +135,9 @@ void ContactsWidget::newContact(QString address, QString name)
QString address_entry;
QString name_entry;
for (int i=0; i<num_addresses; i++) {
m_wallet->addressBook()->getRow(i, [&address_entry, &name_entry](const AddressBookInfo &entry){
address_entry = entry.address();
name_entry = entry.description();
m_wallet->addressBook()->getRow(i, [&address_entry, &name_entry](const ContactRow &entry){
address_entry = entry.getAddress();
name_entry = entry.getLabel();
});
if (address == address_entry) {
@ -152,7 +152,7 @@ void ContactsWidget::newContact(QString address, QString name)
}
}
m_wallet->addressBook()->addRow(address, "", name);
m_wallet->addressBook()->addRow(address, name);
}
void ContactsWidget::deleteContact()

View file

@ -86,7 +86,7 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
if (!tx) return;
bool unconfirmed = tx->isFailed() || tx->isPending();
if (unconfirmed && tx->direction() != TransactionInfo::Direction_In) {
if (unconfirmed && tx->direction() != TransactionRow::Direction_In) {
menu.addAction("Resend transaction", this, &HistoryWidget::onResendTransaction);
}

View file

@ -22,7 +22,7 @@
#include "dialog/WalletInfoDialog.h"
#include "dialog/WalletCacheDebugDialog.h"
#include "libwalletqt/AddressBook.h"
#include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/rows/CoinsInfo.h"
#include "libwalletqt/Transfer.h"
#include "utils/AppData.h"
#include "utils/AsyncTask.h"
@ -502,20 +502,20 @@ void MainWindow::onWalletOpened() {
m_wallet->subaddressModel()->setCurrentSubaddressAccount(m_wallet->currentSubaddressAccount());
// history page
m_wallet->history()->refresh(m_wallet->currentSubaddressAccount());
m_wallet->history()->refresh();
// coins page
m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount());
m_wallet->coins()->refresh();
m_coinsWidget->setModel(m_wallet->coinsModel(), m_wallet->coins());
m_wallet->coinsModel()->setCurrentSubaddressAccount(m_wallet->currentSubaddressAccount());
// Coin labeling uses set_tx_note, so we need to refresh history too
connect(m_wallet->coins(), &Coins::descriptionChanged, [this] {
m_wallet->history()->refresh(m_wallet->currentSubaddressAccount());
m_wallet->history()->refresh();
});
// Vice versa
connect(m_wallet->history(), &TransactionHistory::txNoteChanged, [this] {
m_wallet->coins()->refresh(m_wallet->currentSubaddressAccount());
m_wallet->coins()->refresh();
});
this->updatePasswordIcon();
@ -898,7 +898,7 @@ void MainWindow::onTransactionCommitted(bool success, PendingTransaction *tx, co
msgBox.exec();
if (msgBox.clickedButton() == showDetailsButton) {
this->showHistoryTab();
TransactionInfo *txInfo = m_wallet->history()->transaction(txid.first());
TransactionRow *txInfo = m_wallet->history()->transaction(txid.first());
auto *dialog = new TxInfoDialog(m_wallet, txInfo, this);
connect(dialog, &TxInfoDialog::resendTranscation, this, &MainWindow::onResendTransaction);
dialog->show();
@ -1145,7 +1145,7 @@ void MainWindow::importContacts() {
i.next();
bool addressValid = WalletManager::addressValid(i.value(), m_wallet->nettype());
if(addressValid) {
m_wallet->addressBook()->addRow(i.value(), "", i.key());
m_wallet->addressBook()->addRow(i.value(), i.key());
inserts++;
}
}

View file

@ -64,7 +64,7 @@ void AccountSwitcherDialog::switchAccount() {
return;
}
m_wallet->switchSubaddressAccount(row->getRowId());
m_wallet->switchSubaddressAccount(row->getRow());
}
void AccountSwitcherDialog::copyLabel() {
@ -73,7 +73,7 @@ void AccountSwitcherDialog::copyLabel() {
return;
}
Utils::copyToClipboard(QString::fromStdString(row->getLabel()));
Utils::copyToClipboard(row->getLabel());
}
void AccountSwitcherDialog::copyBalance() {
@ -82,7 +82,7 @@ void AccountSwitcherDialog::copyBalance() {
return;
}
Utils::copyToClipboard(QString::fromStdString(row->getBalance()));
Utils::copyToClipboard(row->getBalance());
}
void AccountSwitcherDialog::editLabel() {
@ -93,6 +93,9 @@ void AccountSwitcherDialog::editLabel() {
void AccountSwitcherDialog::updateSelection() {
QModelIndex index = m_model->index(m_wallet->currentSubaddressAccount(), 0);
if (!index.isValid()) {
return;
}
ui->accounts->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
@ -111,7 +114,7 @@ void AccountSwitcherDialog::showContextMenu(const QPoint &point) {
menu->popup(ui->accounts->viewport()->mapToGlobal(point));
}
Monero::SubaddressAccountRow* AccountSwitcherDialog::currentEntry() {
AccountRow* AccountSwitcherDialog::currentEntry() {
QModelIndex index = m_proxyModel->mapToSource(ui->accounts->currentIndex());
return m_wallet->subaddressAccountModel()->entryFromIndex(index);
}

View file

@ -34,7 +34,7 @@ private:
void copyBalance();
void editLabel();
Monero::SubaddressAccountRow* currentEntry();
AccountRow* currentEntry();
QScopedPointer<Ui::AccountSwitcherDialog> ui;
Wallet *m_wallet;

View file

@ -16,9 +16,6 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="accounts">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>

View file

@ -8,7 +8,7 @@
#include "components.h"
#include "libwalletqt/Coins.h"
#include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/rows/CoinsInfo.h"
namespace Ui {
class OutputInfoDialog;

View file

@ -7,7 +7,7 @@
#include <QDialog>
#include "components.h"
#include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/rows/CoinsInfo.h"
namespace Ui {
class OutputSweepDialog;

View file

@ -10,14 +10,14 @@
#include "config.h"
#include "constants.h"
#include "libwalletqt/Coins.h"
#include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/rows/CoinsInfo.h"
#include "libwalletqt/TransactionHistory.h"
#include "libwalletqt/Transfer.h"
#include "libwalletqt/WalletManager.h"
#include "utils/Icons.h"
#include "utils/Utils.h"
TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent)
TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *parent)
: QDialog(parent)
, ui(new Ui::TxInfoDialog)
, m_wallet(wallet)
@ -41,7 +41,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *par
this->setData(txInfo);
if ((txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionInfo::Direction_In) {
if ((txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionRow::Direction_In) {
connect(ui->btn_rebroadcastTx, &QPushButton::pressed, [this]{
emit resendTranscation(m_txid);
});
@ -49,7 +49,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *par
ui->btn_rebroadcastTx->hide();
}
if (txInfo->direction() == TransactionInfo::Direction_In) {
if (txInfo->direction() == TransactionRow::Direction_In) {
ui->btn_CopyTxKey->setDisabled(true);
ui->btn_CopyTxKey->setToolTip("No tx secret key available for incoming transactions.");
}
@ -106,7 +106,7 @@ void TxInfoDialog::adjustHeight(QTextEdit *textEdit, qreal docHeight) {
textEdit->verticalScrollBar()->hide();
}
void TxInfoDialog::setData(TransactionInfo *tx) {
void TxInfoDialog::setData(TransactionRow *tx) {
QString blockHeight = QString::number(tx->blockHeight());
if (tx->isFailed()) {
@ -131,13 +131,13 @@ void TxInfoDialog::setData(TransactionInfo *tx) {
ui->label_lock->setText("Lock: Outputs are spendable");
}
QString direction = tx->direction() == TransactionInfo::Direction_In ? "received" : "sent";
QString direction = tx->direction() == TransactionRow::Direction_In ? "received" : "sent";
ui->label_amount->setText(QString("Amount %1: %2 XMR").arg(direction, tx->displayAmount()));
QString fee;
if (tx->isCoinbase())
fee = "Not applicable";
else if (tx->direction() == TransactionInfo::Direction_In)
else if (tx->direction() == TransactionRow::Direction_In)
fee = "Paid by sender";
else if (tx->fee().isEmpty())
fee = "N/A";
@ -149,7 +149,7 @@ void TxInfoDialog::setData(TransactionInfo *tx) {
}
void TxInfoDialog::updateData() {
TransactionInfo *tx = m_wallet->history()->transaction(m_txid);
TransactionRow *tx = m_wallet->history()->transaction(m_txid);
if (!tx) return;
this->setData(tx);
}

View file

@ -10,6 +10,7 @@
#include <QSvgWidget>
#include "dialog/TxProofDialog.h"
#include "libwalletqt/rows/TransactionRow.h"
namespace Ui {
class TxInfoDialog;
@ -20,7 +21,7 @@ class TxInfoDialog : public QDialog
Q_OBJECT
public:
explicit TxInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent = nullptr);
explicit TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *parent = nullptr);
~TxInfoDialog() override;
signals:
@ -30,14 +31,14 @@ private:
void copyTxID();
void copyTxKey();
void createTxProof();
void setData(TransactionInfo *tx);
void setData(TransactionRow *tx);
void updateData();
void adjustHeight(QTextEdit *textEdit, qreal docHeight);
void viewOnBlockExplorer();
QScopedPointer<Ui::TxInfoDialog> ui;
Wallet *m_wallet;
TransactionInfo *m_txInfo;
TransactionRow *m_txInfo;
TxProofDialog *m_txProofDialog;
QString m_txid;
};

View file

@ -10,7 +10,7 @@
#include "utils/Icons.h"
#include "utils/Utils.h"
TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txInfo)
TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, TransactionRow *txInfo)
: WindowModalDialog(parent)
, ui(new Ui::TxProofDialog)
, m_wallet(wallet)
@ -70,7 +70,7 @@ void TxProofDialog::selectSpendProof() {
m_mode = Mode::SpendProof;
this->resetFrames();
if (m_direction == TransactionInfo::Direction_In) {
if (m_direction == TransactionRow::Direction_In) {
this->showWarning("Your wallet did not construct this transaction. Creating a SpendProof is not possible.");
return;
}
@ -107,7 +107,7 @@ void TxProofDialog::selectInProof() {
m_mode = Mode::InProof;
this->resetFrames();
if (m_direction == TransactionInfo::Direction_Out) {
if (m_direction == TransactionRow::Direction_Out) {
this->showWarning("Can't create InProofs for outgoing transactions.");
return;
}

View file

@ -7,8 +7,8 @@
#include <QDialog>
#include "components.h"
#include "libwalletqt/TransactionInfo.h"
#include "libwalletqt/Wallet.h"
#include "rows/TransactionRow.h"
namespace Ui {
class TxProofDialog;
@ -19,7 +19,7 @@ class TxProofDialog : public WindowModalDialog
Q_OBJECT
public:
explicit TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txid);
explicit TxProofDialog(QWidget *parent, Wallet *wallet, TransactionRow *txid);
~TxProofDialog() override;
void setTxId(const QString &txid);
void getTxKey();
@ -52,7 +52,7 @@ private:
QString m_txid;
QString m_txKey;
Mode m_mode;
TransactionInfo::Direction m_direction;
TransactionRow::Direction m_direction;
};
#endif //FEATHER_TXPROOFDIALOG_H

View file

@ -4,48 +4,54 @@
#include "AddressBook.h"
#include <QDebug>
AddressBook::AddressBook(Monero::AddressBook *abImpl, QObject *parent)
: QObject(parent), m_addressBookImpl(abImpl)
AddressBook::AddressBook(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
: QObject(parent)
, m_wallet(wallet)
, m_wallet2(wallet2)
{
getAll();
this->refresh();
}
QString AddressBook::errorString() const
{
return QString::fromStdString(m_addressBookImpl->errorString());
return m_errorString;
}
int AddressBook::errorCode() const
AddressBook::ErrorCode AddressBook::errorCode() const
{
return m_addressBookImpl->errorCode();
return m_errorCode;
}
void AddressBook::getAll()
void AddressBook::refresh()
{
emit refreshStarted();
{
QWriteLocker locker(&m_lock);
clearRows();
qDeleteAll(m_rows);
// Fetch from Wallet2 and create vector of AddressBookRow objects
std::vector<tools::wallet2::address_book_row> rows = m_wallet2->get_address_book();
for (qsizetype i = 0; i < rows.size(); ++i) {
tools::wallet2::address_book_row *row = &rows.at(i);
m_addresses.clear();
m_rows.clear();
std::string address;
if (row->m_has_payment_id)
address = cryptonote::get_account_integrated_address_as_str(m_wallet2->nettype(), row->m_address, row->m_payment_id);
else
address = get_account_address_as_str(m_wallet2->nettype(), row->m_is_subaddress, row->m_address);
for (auto &abr: m_addressBookImpl->getAll()) {
m_addresses.insert(QString::fromStdString(abr->getAddress()), m_rows.size());
m_rows.append(new AddressBookInfo(abr, this));
}
auto* abr = new ContactRow{this,
i,
QString::fromStdString(address),
QString::fromStdString(row->m_description)};
m_rows.push_back(abr);
}
emit refreshFinished();
}
bool AddressBook::getRow(int index, std::function<void (AddressBookInfo &)> callback) const
bool AddressBook::getRow(int index, std::function<void (ContactRow &)> callback) const
{
QReadLocker locker(&m_lock);
if (index < 0 || index >= m_rows.size())
{
return false;
@ -55,88 +61,58 @@ bool AddressBook::getRow(int index, std::function<void (AddressBookInfo &)> call
return true;
}
bool AddressBook::addRow(const QString &address, const QString &payment_id, const QString &description)
bool AddressBook::addRow(const QString &address, const QString &description)
{
// virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0;
bool result;
m_errorString = "";
{
QWriteLocker locker(&m_lock);
result = m_addressBookImpl->addRow(address.toStdString(), payment_id.toStdString(), description.toStdString());
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, m_wallet2->nettype(), address.toStdString())) {
m_errorString = tr("Invalid destination address");
m_errorCode = Invalid_Address;
return false;
}
if (result)
{
getAll();
bool r = m_wallet2->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : nullptr, description.toStdString(), info.is_subaddress);
if (r)
refresh();
else
m_errorCode = General_Error;
return r;
}
return result;
bool AddressBook::setDescription(int index, const QString &description) {
m_errorString = "";
const auto ab = m_wallet2->get_address_book();
if (index >= ab.size()){
return false;
}
void AddressBook::setDescription(int index, const QString &description) {
bool result;
{
QWriteLocker locker(&m_lock);
result = m_addressBookImpl->setDescription(index, description.toStdString());
}
if (result)
{
getAll();
emit descriptionChanged();
}
tools::wallet2::address_book_row entry = ab[index];
entry.m_description = description.toStdString();
bool r = m_wallet2->set_address_book_row(index, entry.m_address, entry.m_has_payment_id ? &entry.m_payment_id : nullptr, entry.m_description, entry.m_is_subaddress);
if (r)
refresh();
else
m_errorCode = General_Error;
return r;
}
bool AddressBook::deleteRow(int rowId)
{
bool result;
bool r = m_wallet2->delete_address_book_row(rowId);
if (r)
refresh();
return r;
}
qsizetype AddressBook::count() const
{
QWriteLocker locker(&m_lock);
result = m_addressBookImpl->deleteRow(rowId);
return m_rows.length();
}
// Fetch new data from wallet2.
if (result)
void AddressBook::clearRows()
{
getAll();
}
return result;
}
quint64 AddressBook::count() const
{
QReadLocker locker(&m_lock);
return m_rows.size();
}
QString AddressBook::getDescription(const QString &address) const
{
QReadLocker locker(&m_lock);
const QMap<QString, size_t>::const_iterator it = m_addresses.find(address);
if (it == m_addresses.end())
{
return {};
}
return m_rows.value(*it)->description();
}
QString AddressBook::getAddress(const QString &description) const
{
QReadLocker locker(&m_lock);
for (const auto &row : m_rows) {
if (row->description() == description) {
return row->address();
}
}
return QString();
qDeleteAll(m_rows);
m_rows.clear();
}

View file

@ -5,43 +5,44 @@
#define ADDRESSBOOK_H
#include <wallet/api/wallet2_api.h>
#include "AddressBookInfo.h"
#include <QMap>
#include <QObject>
#include <QReadWriteLock>
#include <QList>
#include <QDateTime>
#include "rows/ContactRow.h"
#include "Wallet.h"
#include "wallet/wallet2.h"
namespace Monero {
struct AddressBook;
}
class AddressBookRow;
class AddressBook : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE bool getRow(int index, std::function<void (AddressBookInfo &)> callback) const;
Q_INVOKABLE bool addRow(const QString &address, const QString &payment_id, const QString &description);
Q_INVOKABLE bool deleteRow(int rowId);
Q_INVOKABLE void setDescription(int index, const QString &label);
quint64 count() const;
Q_INVOKABLE QString errorString() const;
Q_INVOKABLE int errorCode() const;
Q_INVOKABLE QString getDescription(const QString &address) const;
Q_INVOKABLE QString getAddress(const QString &description) const;
public:
enum ErrorCode {
Status_Ok,
General_Error,
Invalid_Address,
Invalid_Payment_Id
};
Q_ENUM(ErrorCode);
private:
void getAll();
bool getRow(int index, std::function<void (ContactRow &)> callback) const;
bool addRow(const QString &address, const QString &description);
bool deleteRow(int rowId);
bool setDescription(int index, const QString &label);
qsizetype count() const;
QString errorString() const;
ErrorCode errorCode() const;
void refresh();
void clearRows();
signals:
void refreshStarted() const;
@ -49,12 +50,15 @@ signals:
void descriptionChanged() const;
private:
explicit AddressBook(Monero::AddressBook * abImpl, QObject *parent);
explicit AddressBook(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent);
friend class Wallet;
Monero::AddressBook * m_addressBookImpl;
mutable QReadWriteLock m_lock;
QList<AddressBookInfo*> m_rows;
QMap<QString, size_t> m_addresses;
Wallet *m_wallet;
tools::wallet2 *m_wallet2;
QList<ContactRow*> m_rows;
QString m_errorString;
ErrorCode m_errorCode;
};
#endif // ADDRESSBOOK_H

View file

@ -1,20 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "AddressBookInfo.h"
QString AddressBookInfo::address() const {
return m_address;
}
QString AddressBookInfo::description() const {
return m_description;
}
AddressBookInfo::AddressBookInfo(const Monero::AddressBookRow *pimpl, QObject *parent)
: QObject(parent)
, m_address(QString::fromStdString(pimpl->getAddress()))
, m_description(QString::fromStdString(pimpl->getDescription()))
{
}

View file

@ -1,28 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#ifndef FEATHER_ADDRESSBOOKINFO_H
#define FEATHER_ADDRESSBOOKINFO_H
#include <wallet/api/wallet2_api.h>
#include <QObject>
class AddressBookInfo : public QObject {
Q_OBJECT
Q_PROPERTY(QString address READ address);
Q_PROPERTY(QString description READ description);
public:
QString address() const;
QString description() const;
private:
explicit AddressBookInfo(const Monero::AddressBookRow *pimpl, QObject *parent = nullptr);
friend class AddressBook;
QString m_address;
QString m_description;
};
#endif //FEATHER_ADDRESSBOOKINFO_H

View file

@ -2,50 +2,80 @@
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "Coins.h"
#include "rows/CoinsInfo.h"
#include <QDebug>
#include "CoinsInfo.h"
#include <QFile>
Coins::Coins(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
: QObject(parent)
, m_wallet(wallet)
, m_wallet2(wallet2)
{
}
bool Coins::coin(int index, std::function<void (CoinsInfo &)> callback)
{
QReadLocker locker(&m_lock);
if (index < 0 || index >= m_tinfo.size()) {
if (index < 0 || index >= m_rows.size()) {
qCritical("%s: no transaction info for index %d", __FUNCTION__, index);
qCritical("%s: there's %d transactions in backend", __FUNCTION__, m_pimpl->count());
qCritical("%s: there's %lld transactions in backend", __FUNCTION__, m_rows.count());
return false;
}
callback(*m_tinfo.value(index));
callback(*m_rows.value(index));
return true;
}
CoinsInfo* Coins::coin(int index)
{
return m_tinfo.value(index);
return m_rows.value(index);
}
void Coins::refresh(quint32 accountIndex)
void Coins::refresh()
{
emit refreshStarted();
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet2->m_transfers_mutex);
{
QWriteLocker locker(&m_lock);
qDeleteAll(m_tinfo);
m_tinfo.clear();
clearRows();
uint32_t account = m_wallet->currentSubaddressAccount();
m_pimpl->refresh();
for (const auto i : m_pimpl->getAll()) {
if (i->subaddrAccount() != accountIndex) {
for (size_t i = 0; i < m_wallet2->get_num_transfer_details(); ++i)
{
const tools::wallet2::transfer_details &td = m_wallet2->get_transfer_details(i);
if (td.m_subaddr_index.major != account) {
continue;
}
m_tinfo.append(new CoinsInfo(i, this));
auto ci = new CoinsInfo(this);
ci->m_blockHeight = td.m_block_height;
ci->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_txid));
ci->m_internalOutputIndex = td.m_internal_output_index;
ci->m_globalOutputIndex = td.m_global_output_index;
ci->m_spent = td.m_spent;
ci->m_frozen = td.m_frozen;
ci->m_spentHeight = td.m_spent_height;
ci->m_amount = td.m_amount;
ci->m_rct = td.m_rct;
ci->m_keyImageKnown = td.m_key_image_known;
ci->m_pkIndex = td.m_pk_index;
ci->m_subaddrIndex = td.m_subaddr_index.minor;
ci->m_subaddrAccount = td.m_subaddr_index.major;
ci->m_address = QString::fromStdString(m_wallet2->get_subaddress_as_str(td.m_subaddr_index)); // todo: this is expensive, cache maybe?
ci->m_addressLabel = QString::fromStdString(m_wallet2->get_subaddress_label(td.m_subaddr_index));
ci->m_keyImage = QString::fromStdString(epee::string_tools::pod_to_hex(td.m_key_image));
ci->m_unlockTime = td.m_tx.unlock_time;
ci->m_unlocked = m_wallet2->is_transfer_unlocked(td);
ci->m_pubKey = QString::fromStdString(epee::string_tools::pod_to_hex(td.get_public_key()));
ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
ci->m_description = m_wallet->getCacheAttribute(QString("coin.description:%1").arg(ci->m_pubKey));
ci->m_change = m_wallet2->is_change(td);
m_rows.push_back(ci);
}
}
@ -56,9 +86,9 @@ void Coins::refreshUnlocked()
{
QWriteLocker locker(&m_lock);
for (CoinsInfo* c : m_tinfo) {
for (CoinsInfo* c : m_rows) {
if (!c->unlocked()) {
bool unlocked = m_pimpl->isTransferUnlocked(c->unlockTime(), c->blockHeight());
bool unlocked = m_wallet2->is_transfer_unlocked(c->unlockTime(), c->blockHeight());
c->setUnlocked(unlocked);
}
}
@ -68,18 +98,50 @@ quint64 Coins::count() const
{
QReadLocker locker(&m_lock);
return m_tinfo.count();
return m_rows.length();
}
void Coins::freeze(QString &publicKey) const
void Coins::freeze(QString &publicKey)
{
m_pimpl->setFrozen(publicKey.toStdString());
crypto::public_key pk;
if (!epee::string_tools::hex_to_pod(publicKey.toStdString(), pk))
{
qWarning() << "Invalid public key: " << publicKey;
return;
}
try
{
m_wallet2->freeze(pk);
refresh();
}
catch (const std::exception& e)
{
qWarning() << "freeze: " << e.what();
}
emit coinFrozen();
}
void Coins::thaw(QString &publicKey) const
void Coins::thaw(QString &publicKey)
{
m_pimpl->thaw(publicKey.toStdString());
crypto::public_key pk;
if (!epee::string_tools::hex_to_pod(publicKey.toStdString(), pk))
{
qWarning() << "Invalid public key: " << publicKey;
return;
}
try
{
m_wallet2->thaw(pk);
refresh();
}
catch (const std::exception& e)
{
qWarning() << "thaw: " << e.what();
}
emit coinThawed();
}
@ -111,14 +173,12 @@ QVector<CoinsInfo*> Coins::coinsFromKeyImage(const QStringList &keyimages) {
void Coins::setDescription(const QString &publicKey, quint32 accountIndex, const QString &description)
{
m_pimpl->setDescription(publicKey.toStdString(), description.toStdString());
this->refresh(accountIndex);
m_wallet->setCacheAttribute(QString("coin.description:%1").arg(publicKey), description);
this->refresh();
emit descriptionChanged();
}
Coins::Coins(Monero::Coins *pimpl, QObject *parent)
: QObject(parent)
, m_pimpl(pimpl)
{
void Coins::clearRows() {
qDeleteAll(m_rows);
m_rows.clear();
}

View file

@ -12,6 +12,9 @@
#include <QDateTime>
#include <wallet/api/wallet2_api.h>
#include "Wallet.h"
#include "wallet/wallet2.h"
namespace Monero {
struct TransactionHistory;
}
@ -25,15 +28,16 @@ Q_OBJECT
public:
bool coin(int index, std::function<void (CoinsInfo &)> callback);
CoinsInfo * coin(int index);
void refresh(quint32 accountIndex);
void refresh();
void refreshUnlocked();
void freeze(QString &publicKey) const;
void thaw(QString &publicKey) const;
void freeze(QString &publicKey);
void thaw(QString &publicKey);
QVector<CoinsInfo*> coins_from_txid(const QString &txid);
QVector<CoinsInfo*> coinsFromKeyImage(const QStringList &keyimages);
void setDescription(const QString &publicKey, quint32 accountIndex, const QString &description);
quint64 count() const;
void clearRows();
signals:
void refreshStarted() const;
@ -43,13 +47,16 @@ signals:
void descriptionChanged() const;
private:
explicit Coins(Monero::Coins * pimpl, QObject *parent = nullptr);
explicit Coins(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent = nullptr);
private:
friend class Wallet;
Wallet *m_wallet;
tools::wallet2 *m_wallet2;
QList<CoinsInfo*> m_rows;
mutable QReadWriteLock m_lock;
Monero::Coins * m_pimpl;
mutable QList<CoinsInfo*> m_tinfo;
};
#endif //FEATHER_COINS_H

View file

@ -12,14 +12,15 @@
class Ring : public QObject
{
Q_OBJECT
Q_PROPERTY(QString keyImage READ keyImage)
Q_PROPERTY(std::vector<uint64_t> ringMembers READ ringMembers)
private:
public:
explicit Ring(QString _keyImage, std::vector<uint64_t> _ringMembers, QObject *parent = nullptr): QObject(parent), m_keyImage(std::move(_keyImage)), m_ringMembers(std::move(_ringMembers)) {};
private:
friend class TransactionInfo;
QString m_keyImage;
std::vector<uint64_t> m_ringMembers;
public:
QString keyImage() const { return m_keyImage; }
std::vector<uint64_t> ringMembers() const { return m_ringMembers; }

View file

@ -159,8 +159,8 @@ void Subaddress::clearRows() {
SubaddressRow* Subaddress::row(int index) const {
return m_rows.value(index);
};
}
QString Subaddress::getError() const {
return m_errorString;
};
}

View file

@ -4,31 +4,15 @@
#include "SubaddressAccount.h"
#include <QDebug>
SubaddressAccount::SubaddressAccount(Monero::SubaddressAccount *subaddressAccountImpl, QObject *parent)
: QObject(parent), m_subaddressAccountImpl(subaddressAccountImpl)
SubaddressAccount::SubaddressAccount(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
: QObject(parent)
, m_wallet(wallet)
, m_wallet2(wallet2)
{
getAll();
}
void SubaddressAccount::getAll() const
bool SubaddressAccount::getRow(int index, std::function<void (AccountRow &row)> callback) const
{
emit refreshStarted();
{
QWriteLocker locker(&m_lock);
m_rows.clear();
for (auto &row: m_subaddressAccountImpl->getAll()) {
m_rows.append(row);
}
}
emit refreshFinished();
}
bool SubaddressAccount::getRow(int index, std::function<void (Monero::SubaddressAccountRow &)> callback) const
{
QReadLocker locker(&m_lock);
if (index < 0 || index >= m_rows.size())
{
return false;
@ -38,32 +22,50 @@ bool SubaddressAccount::getRow(int index, std::function<void (Monero::Subaddress
return true;
}
void SubaddressAccount::addRow(const QString &label) const
void SubaddressAccount::addRow(const QString &label)
{
m_subaddressAccountImpl->addRow(label.toStdString());
getAll();
m_wallet2->add_subaddress_account(label.toStdString());
refresh();
}
void SubaddressAccount::setLabel(quint32 accountIndex, const QString &label) const
void SubaddressAccount::setLabel(quint32 accountIndex, const QString &label)
{
m_subaddressAccountImpl->setLabel(accountIndex, label.toStdString());
getAll();
m_wallet2->set_subaddress_label({accountIndex, 0}, label.toStdString());
refresh();
}
void SubaddressAccount::refresh() const
void SubaddressAccount::refresh()
{
m_subaddressAccountImpl->refresh();
getAll();
emit refreshStarted();
this->clearRows();
for (uint32_t i = 0; i < m_wallet2->get_num_subaddress_accounts(); ++i)
{
auto *row = new AccountRow{this,
i,
QString::fromStdString(m_wallet2->get_subaddress_as_str({i,0})),
QString::fromStdString(m_wallet2->get_subaddress_label({i,0})),
m_wallet2->balance(i, false),
m_wallet2->unlocked_balance(i, false)};
m_rows.append(row);
}
quint64 SubaddressAccount::count() const
{
QReadLocker locker(&m_lock);
return m_rows.size();
emit refreshFinished();
}
Monero::SubaddressAccountRow* SubaddressAccount::row(int index) const
qsizetype SubaddressAccount::count() const
{
return m_rows.length();
}
void SubaddressAccount::clearRows()
{
qDeleteAll(m_rows);
m_rows.clear();
}
AccountRow* SubaddressAccount::row(int index) const
{
return m_rows.value(index);
}

View file

@ -12,30 +12,40 @@
#include <QList>
#include <QDateTime>
#include <wallet/wallet2.h>
#include "Wallet.h"
#include "rows/AccountRow.h"
class SubaddressAccount : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void getAll() const;
Q_INVOKABLE bool getRow(int index, std::function<void (Monero::SubaddressAccountRow &)> callback) const;
Q_INVOKABLE void addRow(const QString &label) const;
Q_INVOKABLE void setLabel(quint32 accountIndex, const QString &label) const;
Q_INVOKABLE void refresh() const;
quint64 count() const;
Monero::SubaddressAccountRow* row(int index) const;
void getAll() const;
bool getRow(int index, std::function<void (AccountRow &row)> callback) const;
void addRow(const QString &label);
void setLabel(quint32 accountIndex, const QString &label);
void refresh();
qsizetype count() const;
void clearRows();
AccountRow* row(int index) const;
signals:
void refreshStarted() const;
void refreshFinished() const;
public slots:
private:
explicit SubaddressAccount(Monero::SubaddressAccount * subaddressAccountImpl, QObject *parent);
explicit SubaddressAccount(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent);
friend class Wallet;
mutable QReadWriteLock m_lock;
Monero::SubaddressAccount * m_subaddressAccountImpl;
mutable QList<Monero::SubaddressAccountRow*> m_rows;
Wallet *m_wallet;
tools::wallet2 *m_wallet2;
QList<AccountRow*> m_rows;
};
#endif // SUBADDRESSACCOUNT_H

View file

@ -2,48 +2,49 @@
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "TransactionHistory.h"
#include "TransactionInfo.h"
#include "utils/Utils.h"
#include "utils/AppData.h"
#include "utils/config.h"
#include "constants.h"
#include "WalletManager.h"
#include "Transfer.h"
#include "Ring.h"
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
bool TransactionHistory::transaction(int index, std::function<void (TransactionRow &)> callback)
{
QReadLocker locker(&m_lock);
if (index < 0 || index >= m_tinfo.size()) {
if (index < 0 || index >= m_rows.size()) {
qCritical("%s: no transaction info for index %d", __FUNCTION__, index);
qCritical("%s: there's %d transactions in backend", __FUNCTION__, m_pimpl->count());
qCritical("%s: there's %d transactions in backend", __FUNCTION__, this->count());
return false;
}
callback(*m_tinfo.value(index));
callback(*m_rows.value(index));
return true;
}
TransactionInfo* TransactionHistory::transaction(const QString &id)
TransactionRow* TransactionHistory::transaction(const QString &id)
{
QReadLocker locker(&m_lock);
auto itr = std::find_if(m_tinfo.begin(), m_tinfo.end(),
[&](const TransactionInfo * ti) {
auto itr = std::find_if(m_rows.begin(), m_rows.end(),
[&](const TransactionRow * ti) {
return ti->hash() == id;
});
return itr != m_tinfo.end() ? *itr : nullptr;
return itr != m_rows.end() ? *itr : nullptr;
}
TransactionInfo* TransactionHistory::transaction(int index)
TransactionRow* TransactionHistory::transaction(int index)
{
if (index < 0 || index >= m_tinfo.size()) {
if (index < 0 || index >= m_rows.size()) {
return nullptr;
}
return m_tinfo[index];
return m_rows[index];
}
void TransactionHistory::refresh(quint32 accountIndex)
void TransactionHistory::refresh()
{
QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay();
QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
@ -53,55 +54,214 @@ void TransactionHistory::refresh(quint32 accountIndex)
{
QWriteLocker locker(&m_lock);
qDeleteAll(m_tinfo);
m_tinfo.clear();
clearRows();
quint64 lastTxHeight = 0;
m_locked = false;
m_minutesToUnlock = 0;
m_pimpl->refresh();
for (const auto i : m_pimpl->getAll()) {
if (i->subaddrAccount() != accountIndex) {
uint64_t min_height = 0;
uint64_t max_height = (uint64_t)-1;
uint64_t wallet_height = m_wallet->blockChainHeight();
uint32_t account = m_wallet->currentSubaddressAccount();
// transactions are stored in wallet2:
// - confirmed_transfer_details - out transfers
// - unconfirmed_transfer_details - pending out transfers
// - payment_details - input transfers
// payments are "input transactions";
// one input transaction contains only one transfer. e.g. <transaction_id> - <100XMR>
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> in_payments;
m_wallet2->get_payments(in_payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = in_payments.begin(); i != in_payments.end(); ++i)
{
const tools::wallet2::payment_details &pd = i->second;
if (pd.m_subaddr_index.major != account) {
continue;
}
m_tinfo.append(new TransactionInfo(i, this));
std::string payment_id = epee::string_tools::pod_to_hex(i->first);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
const TransactionInfo *ti = m_tinfo.back();
// looking for transactions timestamp scope
if (ti->timestamp() >= lastDateTime) {
lastDateTime = ti->timestamp();
auto* t = new TransactionRow();
t->m_paymentId = QString::fromStdString(payment_id);
t->m_coinbase = pd.m_coinbase;
t->m_amount = pd.m_amount;
t->m_fee = pd.m_fee;
t->m_direction = TransactionRow::Direction_In;
t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(pd.m_tx_hash));
t->m_blockHeight = pd.m_block_height;
t->m_description = QString::fromStdString(m_wallet2->get_tx_note(pd.m_tx_hash));
t->m_subaddrIndex = { pd.m_subaddr_index.minor };
t->m_subaddrAccount = pd.m_subaddr_index.major;
t->m_label = QString::fromStdString(m_wallet2->get_subaddress_label(pd.m_subaddr_index));
t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t->m_confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0;
t->m_unlockTime = pd.m_unlock_time;
m_rows.append(t);
}
if (ti->timestamp() <= firstDateTime) {
firstDateTime = ti->timestamp();
// confirmed output transactions
// one output transaction may contain more than one money transfer, e.g.
// <transaction_id>:
// transfer1: 100XMR to <address_1>
// transfer2: 50XMR to <address_2>
// fee: fee charged per transaction
//
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> out_payments;
m_wallet2->get_payments_out(out_payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = out_payments.begin();
i != out_payments.end(); ++i) {
const crypto::hash &hash = i->first;
const tools::wallet2::confirmed_transfer_details &pd = i->second;
if (pd.m_subaddr_account != account) {
continue;
}
quint64 requiredConfirmations = (ti->blockHeight() < ti->unlockTime()) ? ti->unlockTime() - ti->blockHeight() : 10;
// store last tx height
if (ti->confirmations() < requiredConfirmations && ti->blockHeight() >= lastTxHeight) {
lastTxHeight = ti->blockHeight();
// TODO: Fetch block time and confirmations needed from wallet2?
m_minutesToUnlock = (requiredConfirmations - ti->confirmations()) * 2;
m_locked = true;
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
std::string payment_id = epee::string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
auto* t = new TransactionRow();
t->m_paymentId = QString::fromStdString(payment_id);
t->m_amount = pd.m_amount_in - change - fee;
t->m_fee = fee;
t->m_direction = TransactionRow::Direction_Out;
t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(hash));
t->m_blockHeight = pd.m_block_height;
t->m_description = QString::fromStdString(m_wallet2->get_tx_note(hash));
t->m_subaddrAccount = pd.m_subaddr_account;
t->m_label = QString::fromStdString(pd.m_subaddr_indices.size() == 1 ? m_wallet2->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : "");
t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t->m_confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0;
for (uint32_t idx : t->subaddrIndex())
{
t->m_subaddrIndex.insert(idx);
}
// single output transaction might contain multiple transfers
for (auto const &d: pd.m_dests)
{
Transfer *transfer = new Transfer(d.amount, QString::fromStdString(d.address(m_wallet2->nettype(), pd.m_payment_id)), this);
t->m_transfers.append(transfer);
}
for (auto const &r: pd.m_rings)
{
Ring *ring = new Ring(QString::fromStdString(epee::string_tools::pod_to_hex(r.first)), cryptonote::relative_output_offsets_to_absolute(r.second), this);
t->m_rings.append(ring);
}
m_rows.append(t);
}
// unconfirmed output transactions
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments_out;
m_wallet2->get_unconfirmed_payments_out(upayments_out);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments_out.begin(); i != upayments_out.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
if (pd.m_subaddr_account != account) {
continue;
}
const crypto::hash &hash = i->first;
uint64_t amount = pd.m_amount_in;
uint64_t fee = amount - pd.m_amount_out;
std::string payment_id = epee::string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
auto *t = new TransactionRow();
t->m_paymentId = QString::fromStdString(payment_id);
t->m_amount = amount - pd.m_change - fee;
t->m_fee = fee;
t->m_direction = TransactionRow::Direction_Out;
t->m_failed = is_failed;
t->m_pending = true;
t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(hash));
t->m_description = QString::fromStdString(m_wallet2->get_tx_note(hash));
t->m_subaddrAccount = pd.m_subaddr_account;
t->m_label = QString::fromStdString(pd.m_subaddr_indices.size() == 1 ? m_wallet2->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : "");
t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t->m_confirmations = 0;
for (uint32_t idx : t->subaddrIndex())
{
t->m_subaddrIndex.insert(idx);
}
for (auto const &d: pd.m_dests)
{
Transfer *transfer = new Transfer(d.amount, QString::fromStdString(d.address(m_wallet2->nettype(), pd.m_payment_id)), this);
t->m_transfers.append(transfer);
}
for (auto const &r: pd.m_rings)
{
Ring *ring = new Ring(QString::fromStdString(epee::string_tools::pod_to_hex(r.first)), cryptonote::relative_output_offsets_to_absolute(r.second), this);
t->m_rings.append(ring);
}
m_rows.append(t);
}
// unconfirmed payments (tx pool)
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> upayments;
m_wallet2->get_unconfirmed_payments(upayments);
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
const tools::wallet2::payment_details &pd = i->second.m_pd;
if (pd.m_subaddr_index.major != account) {
continue;
}
std::string payment_id = epee::string_tools::pod_to_hex(i->first);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
auto *t = new TransactionRow();
t->m_paymentId = QString::fromStdString(payment_id);
t->m_amount = pd.m_amount;
t->m_direction = TransactionRow::Direction_In;
t->m_hash = QString::fromStdString(epee::string_tools::pod_to_hex(pd.m_tx_hash));
t->m_blockHeight = pd.m_block_height;
t->m_description = QString::fromStdString(m_wallet2->get_tx_note(pd.m_tx_hash));
t->m_pending = true;
t->m_subaddrIndex = { pd.m_subaddr_index.minor };
t->m_subaddrAccount = pd.m_subaddr_index.major;
t->m_label = QString::fromStdString(m_wallet2->get_subaddress_label(pd.m_subaddr_index));
t->m_timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t->m_confirmations = 0;
m_rows.append(t);
LOG_PRINT_L1(__FUNCTION__ << ": Unconfirmed payment found " << pd.m_amount);
}
}
emit refreshFinished();
if (m_firstDateTime != firstDateTime) {
m_firstDateTime = firstDateTime;
emit firstDateTimeChanged();
}
if (m_lastDateTime != lastDateTime) {
m_lastDateTime = lastDateTime;
emit lastDateTimeChanged();
}
}
void TransactionHistory::setTxNote(const QString &txid, const QString &note)
{
m_pimpl->setTxNote(txid.toStdString(), note.toStdString());
cryptonote::blobdata txid_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(txid.toStdString(), txid_data) || txid_data.size() != sizeof(crypto::hash))
return;
const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
m_wallet2->set_tx_note(htxid, note.toStdString());
refresh();
emit txNoteChanged();
}
@ -109,7 +269,7 @@ quint64 TransactionHistory::count() const
{
QReadLocker locker(&m_lock);
return m_tinfo.count();
return m_rows.length();
}
QDateTime TransactionHistory::firstDateTime() const
@ -133,8 +293,12 @@ bool TransactionHistory::TransactionHistory::locked() const
}
TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent)
: QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false)
TransactionHistory::TransactionHistory(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
: QObject(parent)
, m_wallet(wallet)
, m_wallet2(wallet2)
, m_minutesToUnlock(0)
, m_locked(false)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
m_firstDateTime = QDate(2014, 4, 18).startOfDay();
@ -144,22 +308,25 @@ TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObjec
m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
}
void TransactionHistory::clearRows() {
qDeleteAll(m_rows);
m_rows.clear();
}
bool TransactionHistory::writeCSV(const QString &path) {
QString data;
QReadLocker locker(&m_lock);
auto transactions = m_pimpl->getAll();
auto transactions = m_rows;
std::sort(transactions.begin(), transactions.end(), [](const Monero::TransactionInfo *info1, const Monero::TransactionInfo *info2){
std::sort(m_rows.begin(), m_rows.end(), [](const TransactionRow *info1, const TransactionRow *info2){
return info1->blockHeight() < info2->blockHeight();
});
for (const auto &tx : transactions) {
TransactionInfo info(tx, this);
for (const auto &info : transactions) {
// collect column data
QDateTime timeStamp = info.timestamp();
double amount = info.amount();
QDateTime timeStamp = info->timestamp();
double amount = info->amount();
// calc historical fiat price
QString fiatAmount;
@ -176,31 +343,31 @@ bool TransactionHistory::writeCSV(const QString &path) {
fiatAmount = "\"?\"";
QString direction = QString("");
if (info.direction() == TransactionInfo::Direction_In)
if (info->direction() == TransactionRow::Direction_In)
direction = QString("in");
else if (info.direction() == TransactionInfo::Direction_Out)
else if (info->direction() == TransactionRow::Direction_Out)
direction = QString("out");
else
continue; // skip TransactionInfo::Direction_Both
QString displayAmount = info.displayAmount();
QString paymentId = info.paymentId();
QString displayAmount = info->displayAmount();
QString paymentId = info->paymentId();
if (paymentId == "0000000000000000") {
paymentId = "";
}
QString date = QString("%1T%2Z").arg(info.date(), info.time()); // ISO 8601
QString date = QString("%1T%2Z").arg(info->date(), info->time()); // ISO 8601
QString balanceDelta = WalletManager::displayAmount(info.balanceDelta());
if (info.direction() == TransactionInfo::Direction_Out) {
QString balanceDelta = WalletManager::displayAmount(info->balanceDelta());
if (info->direction() == TransactionRow::Direction_Out) {
balanceDelta = "-" + balanceDelta;
}
// format and write
QString line = QString("\n%1,%2,\"%3\",%4,\"%5\",%6,%7,%8,\"%9\",\"%10\",\"%11\",%12,\"%13\"")
.arg(QString::number(info.blockHeight()), QString::number(timeStamp.toSecsSinceEpoch()), date,
QString::number(info.subaddrAccount()), direction, balanceDelta, info.displayAmount(),
info.fee(), info.hash(), info.description(), paymentId, fiatAmount, preferredFiatSymbol);
.arg(QString::number(info->blockHeight()), QString::number(timeStamp.toSecsSinceEpoch()), date,
QString::number(info->subaddrAccount()), direction, balanceDelta, info->displayAmount(),
info->fee(), info->hash(), info->description(), paymentId, fiatAmount, preferredFiatSymbol);
data += line;
}

View file

@ -11,6 +11,10 @@
#include <QReadWriteLock>
#include <QDateTime>
#include "rows/TransactionRow.h"
#include "Wallet.h"
#include "wallet/wallet2.h"
namespace Monero {
struct TransactionHistory;
}
@ -20,24 +24,20 @@ class TransactionInfo;
class TransactionHistory : public QObject
{
Q_OBJECT
Q_PROPERTY(int count READ count)
Q_PROPERTY(QDateTime firstDateTime READ firstDateTime NOTIFY firstDateTimeChanged)
Q_PROPERTY(QDateTime lastDateTime READ lastDateTime NOTIFY lastDateTimeChanged)
Q_PROPERTY(int minutesToUnlock READ minutesToUnlock)
Q_PROPERTY(bool locked READ locked)
public:
Q_INVOKABLE bool transaction(int index, std::function<void (TransactionInfo &)> callback);
Q_INVOKABLE TransactionInfo * transaction(const QString &id);
TransactionInfo* transaction(int index);
Q_INVOKABLE void refresh(quint32 accountIndex);
Q_INVOKABLE void setTxNote(const QString &txid, const QString &note);
Q_INVOKABLE bool writeCSV(const QString &path);
bool transaction(int index, std::function<void (TransactionRow &)> callback);
TransactionRow * transaction(const QString &id);
TransactionRow* transaction(int index);
void refresh();
void setTxNote(const QString &txid, const QString &note);
bool writeCSV(const QString &path);
quint64 count() const;
QDateTime firstDateTime() const;
QDateTime lastDateTime() const;
quint64 minutesToUnlock() const;
bool locked() const;
void clearRows();
signals:
void refreshStarted() const;
@ -47,13 +47,16 @@ signals:
void txNoteChanged() const;
private:
explicit TransactionHistory(Monero::TransactionHistory * pimpl, QObject *parent = nullptr);
explicit TransactionHistory(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent = nullptr);
private:
friend class Wallet;
mutable QReadWriteLock m_lock;
Monero::TransactionHistory * m_pimpl;
mutable QList<TransactionInfo*> m_tinfo;
Wallet *m_wallet;
tools::wallet2 *m_wallet2;
QList<TransactionRow*> m_rows;
mutable QDateTime m_firstDateTime;
mutable QDateTime m_lastDateTime;
mutable int m_minutesToUnlock;

View file

@ -1,202 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "TransactionInfo.h"
#include "libwalletqt/WalletManager.h"
#include "Transfer.h"
#include "Ring.h"
TransactionInfo::Direction TransactionInfo::direction() const
{
return m_direction;
}
bool TransactionInfo::isPending() const
{
return m_pending;
}
bool TransactionInfo::isFailed() const
{
return m_failed;
}
bool TransactionInfo::isCoinbase() const
{
return m_coinbase;
}
quint64 TransactionInfo::balanceDelta() const
{
if (m_direction == Direction_In) {
return m_amount;
}
else if (m_direction == Direction_Out) {
return m_amount + m_fee;
}
return m_amount;
}
double TransactionInfo::amount() const
{
// there's no unsigned uint64 for JS, so better use double
return displayAmount().toDouble();
}
quint64 TransactionInfo::atomicAmount() const
{
return m_amount;
}
QString TransactionInfo::displayAmount() const
{
return WalletManager::displayAmount(m_amount);
}
quint64 TransactionInfo::atomicFee() const
{
return m_fee;
}
QString TransactionInfo::fee() const
{
if(m_fee == 0)
return "";
return WalletManager::displayAmount(m_fee);
}
quint64 TransactionInfo::blockHeight() const
{
return m_blockHeight;
}
QString TransactionInfo::description() const
{
return m_description;
}
QSet<quint32> TransactionInfo::subaddrIndex() const
{
return m_subaddrIndex;
}
quint32 TransactionInfo::subaddrAccount() const
{
return m_subaddrAccount;
}
QString TransactionInfo::label() const
{
return m_label;
}
quint64 TransactionInfo::confirmations() const
{
return m_confirmations;
}
quint64 TransactionInfo::confirmationsRequired() const
{
return (m_blockHeight < m_unlockTime) ? m_unlockTime - m_blockHeight : 10;
}
quint64 TransactionInfo::unlockTime() const
{
return m_unlockTime;
}
QString TransactionInfo::hash() const
{
return m_hash;
}
QDateTime TransactionInfo::timestamp() const
{
return m_timestamp;
}
QString TransactionInfo::date() const
{
return timestamp().date().toString(Qt::ISODate);
}
QString TransactionInfo::time() const
{
return timestamp().time().toString(Qt::ISODate);
}
QString TransactionInfo::paymentId() const
{
return m_paymentId;
}
QString TransactionInfo::destinations_formatted() const
{
QString destinations;
for (auto const& t: m_transfers) {
if (!destinations.isEmpty())
destinations += "<br> ";
destinations += WalletManager::displayAmount(t->amount()) + ": " + t->address();
}
return destinations;
}
QList<QString> TransactionInfo::destinations() const
{
QList<QString> dests;
for (auto const& t: m_transfers) {
dests.append(t->address());
}
return dests;
}
QList<Transfer*> TransactionInfo::transfers() const {
return m_transfers;
}
QString TransactionInfo::rings_formatted() const
{
QString rings;
for (auto const& r: m_rings) {
rings += r->keyImage() + ": \n";
for (uint64_t m : r->ringMembers()){
rings += QString::number(m) + " ";
}
rings += "\n\n";
}
return rings;
}
TransactionInfo::TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent)
: QObject(parent)
, m_amount(pimpl->amount())
, m_blockHeight(pimpl->blockHeight())
, m_description(QString::fromStdString(pimpl->description()))
, m_confirmations(pimpl->confirmations())
, m_direction(static_cast<Direction>(pimpl->direction()))
, m_failed(pimpl->isFailed())
, m_fee(pimpl->fee())
, m_hash(QString::fromStdString(pimpl->hash()))
, m_label(QString::fromStdString(pimpl->label()))
, m_paymentId(QString::fromStdString(pimpl->paymentId()))
, m_pending(pimpl->isPending())
, m_subaddrAccount(pimpl->subaddrAccount())
, m_timestamp(QDateTime::fromSecsSinceEpoch(pimpl->timestamp()))
, m_unlockTime(pimpl->unlockTime())
, m_coinbase(pimpl->isCoinbase())
{
for (auto const &t: pimpl->transfers())
{
Transfer *transfer = new Transfer(t.amount, QString::fromStdString(t.address), this);
m_transfers.append(transfer);
}
for (auto const &r: pimpl->rings())
{
Ring *ring = new Ring(QString::fromStdString(r.first), r.second, this);
m_rings.append(ring);
}
for (uint32_t i : pimpl->subaddrIndex())
{
m_subaddrIndex.insert(i);
}
}

View file

@ -12,7 +12,7 @@ class Transfer : public QObject
{
Q_OBJECT
private:
public:
explicit Transfer(uint64_t _amount, QString _address, QObject *parent = 0)
: QObject(parent), m_amount(_amount), m_address(std::move(_address)) {};
private:

View file

@ -32,21 +32,21 @@ namespace {
Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
: QObject(parent)
, m_walletImpl(wallet)
, m_history(new TransactionHistory(m_walletImpl->history(), this))
, m_history(new TransactionHistory(this, wallet->getWallet(), this))
, m_historyModel(nullptr)
, m_addressBook(new AddressBook(m_walletImpl->addressBook(), this))
, m_addressBook(new AddressBook(this, wallet->getWallet(), this))
, m_addressBookModel(nullptr)
, m_daemonBlockChainHeight(0)
, m_daemonBlockChainTargetHeight(0)
, m_connectionStatus(Wallet::ConnectionStatus_Disconnected)
, m_currentSubaddressAccount(0)
, m_subaddress(new Subaddress(this, wallet->getWallet(), this))
, m_subaddressAccount(new SubaddressAccount(m_walletImpl->subaddressAccount(), this))
, m_subaddressAccount(new SubaddressAccount(this, wallet->getWallet(), this))
, m_refreshNow(false)
, m_refreshEnabled(false)
, m_scheduler(this)
, m_useSSL(true)
, m_coins(new Coins(m_walletImpl->coins(), this))
, m_coins(new Coins(this, wallet->getWallet(), this))
, m_storeTimer(new QTimer(this))
{
m_walletListener = new WalletListenerImpl(this);
@ -71,7 +71,7 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
}
connect(this->history(), &TransactionHistory::txNoteChanged, [this]{
this->history()->refresh(this->currentSubaddressAccount());
this->history()->refresh();
});
connect(this, &Wallet::refreshed, this, &Wallet::onRefreshed);
@ -185,8 +185,8 @@ void Wallet::switchSubaddressAccount(quint32 accountIndex) {
qWarning() << "failed to set " << ATTRIBUTE_SUBADDRESS_ACCOUNT << " cache attribute";
}
m_subaddress->refresh(m_currentSubaddressAccount);
m_history->refresh(m_currentSubaddressAccount);
m_coins->refresh(m_currentSubaddressAccount);
m_history->refresh();
m_coins->refresh();
this->subaddressModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount);
this->coinsModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount);
this->updateBalance();
@ -453,8 +453,8 @@ void Wallet::onRefreshed(bool success, const QString &message) {
}
void Wallet::refreshModels() {
m_history->refresh(this->currentSubaddressAccount());
m_coins->refresh(this->currentSubaddressAccount());
m_history->refresh();
m_coins->refresh();
bool r = this->subaddress()->refresh(this->currentSubaddressAccount());
if (!r) {
@ -776,8 +776,8 @@ void Wallet::onTransactionCommitted(bool success, PendingTransaction *tx, const
// Store wallet immediately, so we don't risk losing tx key if wallet crashes
this->storeSafer();
this->history()->refresh(this->currentSubaddressAccount());
this->coins()->refresh(this->currentSubaddressAccount());
this->history()->refresh();
this->coins()->refresh();
this->updateBalance();
if (!success) {

View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "AccountRow.h"
#include "WalletManager.h"
qsizetype AccountRow::getRow() const {
return m_row;
}
const QString& AccountRow::getAddress() const {
return m_address;
}
const QString& AccountRow::getLabel() const {
return m_label;
}
QString AccountRow::getBalance() const {
return WalletManager::displayAmount(m_balance);
}
QString AccountRow::getUnlockedBalance() const {
return WalletManager::displayAmount(m_unlockedBalance);
}

View file

@ -0,0 +1,36 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#ifndef FEATHER_ACCOUNTROW_H
#define FEATHER_ACCOUNTROW_H
#include <QObject>
class AccountRow : public QObject
{
Q_OBJECT
public:
AccountRow(QObject *parent, qsizetype row, const QString& address, const QString &label, uint64_t balance, uint64_t unlockedBalance)
: QObject(parent)
, m_row(row)
, m_address(address)
, m_label(label)
, m_balance(balance)
, m_unlockedBalance(unlockedBalance) {}
qsizetype getRow() const;
const QString& getAddress() const;
const QString& getLabel() const;
QString getBalance() const;
QString getUnlockedBalance() const;
private:
qsizetype m_row;
QString m_address;
QString m_label;
uint64_t m_balance;
uint64_t m_unlockedBalance;
};
#endif //FEATHER_ACCOUNTROW_H

View file

@ -74,7 +74,15 @@ QString CoinsInfo::address() const {
QString CoinsInfo::addressLabel() const {
if (m_subaddrIndex == 0) {
return m_coinbase ? "Coinbase" : "Change";
if (m_coinbase) {
return "Coinbase";
}
if (m_change) {
return "Change";
}
if (m_addressLabel == "Primary account") {
return "Primary address";
}
}
return m_addressLabel;
@ -108,29 +116,28 @@ QString CoinsInfo::description() const {
return m_description;
}
CoinsInfo::CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent)
bool CoinsInfo::change() const {
return m_change;
}
CoinsInfo::CoinsInfo(QObject *parent)
: QObject(parent)
, m_blockHeight(pimpl->blockHeight())
, m_hash(QString::fromStdString(pimpl->hash()))
, m_internalOutputIndex(pimpl->internalOutputIndex())
, m_globalOutputIndex(pimpl->globalOutputIndex())
, m_spent(pimpl->spent())
, m_frozen(pimpl->frozen())
, m_spentHeight(pimpl->spentHeight())
, m_amount(pimpl->amount())
, m_rct(pimpl->rct())
, m_keyImageKnown(pimpl->keyImageKnown())
, m_pkIndex(pimpl->pkIndex())
, m_subaddrIndex(pimpl->subaddrIndex())
, m_subaddrAccount(pimpl->subaddrAccount())
, m_address(QString::fromStdString(pimpl->address()))
, m_addressLabel(QString::fromStdString(pimpl->addressLabel()))
, m_keyImage(QString::fromStdString(pimpl->keyImage()))
, m_unlockTime(pimpl->unlockTime())
, m_unlocked(pimpl->unlocked())
, m_pubKey(QString::fromStdString(pimpl->pubKey()))
, m_coinbase(pimpl->coinbase())
, m_description(QString::fromStdString(pimpl->description()))
, m_blockHeight(0)
, m_internalOutputIndex(0)
, m_globalOutputIndex(0)
, m_spent(false)
, m_frozen(false)
, m_spentHeight(0)
, m_amount(0)
, m_rct(false)
, m_keyImageKnown(false)
, m_pkIndex(0)
, m_subaddrIndex(0)
, m_subaddrAccount(0)
, m_unlockTime(0)
, m_unlocked(false)
, m_coinbase(false)
, m_change(false)
{
}

View file

@ -14,28 +14,6 @@ class Coins;
class CoinsInfo : public QObject
{
Q_OBJECT
Q_PROPERTY(quint64 blockHeight READ blockHeight)
Q_PROPERTY(QString hash READ hash)
Q_PROPERTY(quint64 internalOutputIndex READ internalOutputIndex)
Q_PROPERTY(quint64 globalOutputIndex READ globalOutputIndex)
Q_PROPERTY(bool spent READ spent)
Q_PROPERTY(bool frozen READ frozen)
Q_PROPERTY(quint64 spentHeight READ spentHeight)
Q_PROPERTY(quint64 amount READ amount)
Q_PROPERTY(QString displayAmount READ displayAmount)
Q_PROPERTY(bool rct READ rct)
Q_PROPERTY(bool keyImageKnown READ keyImageKnown)
Q_PROPERTY(quint64 pkIndex READ pkIndex)
Q_PROPERTY(quint32 subaddrIndex READ subaddrIndex)
Q_PROPERTY(quint32 subaddrAccount READ subaddrAccount)
Q_PROPERTY(QString address READ address)
Q_PROPERTY(QString addressLabel READ addressLabel)
Q_PROPERTY(QString keyImage READ keyImage)
Q_PROPERTY(quint64 unlockTime READ unlockTime)
Q_PROPERTY(bool unlocked READ unlocked)
Q_PROPERTY(QString pubKey READ pubKey)
Q_PROPERTY(bool coinbase READ coinbase)
Q_PROPERTY(QString description READ description)
public:
quint64 blockHeight() const;
@ -60,11 +38,13 @@ public:
QString pubKey() const;
bool coinbase() const;
QString description() const;
bool change() const;
void setUnlocked(bool unlocked);
private:
explicit CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent = nullptr);
explicit CoinsInfo(QObject *parent);
private:
friend class Coins;
@ -89,6 +69,7 @@ private:
QString m_pubKey;
bool m_coinbase;
QString m_description;
bool m_change;
};
#endif //FEATHER_COINSINFO_H

View file

@ -0,0 +1,16 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "ContactRow.h"
qsizetype ContactRow::getRow() const {
return m_row;
}
const QString& ContactRow::getAddress() const {
return m_address;
}
const QString& ContactRow::getLabel() const {
return m_label;
}

View file

@ -0,0 +1,31 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#ifndef FEATHER_CONTACTROW_H
#define FEATHER_CONTACTROW_H
#include <QObject>
class ContactRow : public QObject
{
Q_OBJECT
public:
ContactRow(QObject *parent, qsizetype row, const QString& address, const QString &label)
: QObject(parent)
, m_row(row)
, m_address(address)
, m_label(label) {}
qsizetype getRow() const;
const QString& getAddress() const;
const QString& getLabel() const;
private:
qsizetype m_row;
QString m_address;
QString m_label;
};
#endif //FEATHER_CONTACTROW_H

View file

@ -0,0 +1,183 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "TransactionRow.h"
#include "WalletManager.h"
#include "Transfer.h"
#include "Ring.h"
TransactionRow::TransactionRow()
: m_direction(TransactionRow::Direction_Out)
, m_pending(false)
, m_failed(false)
, m_coinbase(false)
, m_amount(0)
, m_fee(0)
, m_blockHeight(0)
, m_subaddrAccount(0)
, m_confirmations(0)
, m_unlockTime(0)
, m_confirmationsRequired(0)
{
}
TransactionRow::Direction TransactionRow::direction() const
{
return m_direction;
}
bool TransactionRow::isPending() const
{
return m_pending;
}
bool TransactionRow::isFailed() const
{
return m_failed;
}
bool TransactionRow::isCoinbase() const
{
return m_coinbase;
}
quint64 TransactionRow::balanceDelta() const
{
if (m_direction == Direction_In) {
return m_amount;
}
else if (m_direction == Direction_Out) {
return m_amount + m_fee;
}
return m_amount;
}
double TransactionRow::amount() const
{
// there's no unsigned uint64 for JS, so better use double
return displayAmount().toDouble();
}
quint64 TransactionRow::atomicAmount() const
{
return m_amount;
}
QString TransactionRow::displayAmount() const
{
return WalletManager::displayAmount(m_amount);
}
quint64 TransactionRow::atomicFee() const
{
return m_fee;
}
QString TransactionRow::fee() const
{
if(m_fee == 0)
return "";
return WalletManager::displayAmount(m_fee);
}
quint64 TransactionRow::blockHeight() const
{
return m_blockHeight;
}
QString TransactionRow::description() const
{
return m_description;
}
QSet<quint32> TransactionRow::subaddrIndex() const
{
return m_subaddrIndex;
}
quint32 TransactionRow::subaddrAccount() const
{
return m_subaddrAccount;
}
QString TransactionRow::label() const
{
return m_label;
}
quint64 TransactionRow::confirmations() const
{
return m_confirmations;
}
quint64 TransactionRow::confirmationsRequired() const
{
return (m_blockHeight < m_unlockTime) ? m_unlockTime - m_blockHeight : 10;
}
quint64 TransactionRow::unlockTime() const
{
return m_unlockTime;
}
QString TransactionRow::hash() const
{
return m_hash;
}
QDateTime TransactionRow::timestamp() const
{
return m_timestamp;
}
QString TransactionRow::date() const
{
return timestamp().date().toString(Qt::ISODate);
}
QString TransactionRow::time() const
{
return timestamp().time().toString(Qt::ISODate);
}
QString TransactionRow::paymentId() const
{
return m_paymentId;
}
QString TransactionRow::destinations_formatted() const
{
QString destinations;
for (auto const& t: m_transfers) {
if (!destinations.isEmpty())
destinations += "<br> ";
destinations += WalletManager::displayAmount(t->amount()) + ": " + t->address();
}
return destinations;
}
QList<QString> TransactionRow::destinations() const
{
QList<QString> dests;
for (auto const& t: m_transfers) {
dests.append(t->address());
}
return dests;
}
QList<Transfer*> TransactionRow::transfers() const {
return m_transfers;
}
QString TransactionRow::rings_formatted() const
{
QString rings;
for (auto const& r: m_rings) {
rings += r->keyImage() + ": \n";
for (uint64_t m : r->ringMembers()){
rings += QString::number(m) + " ";
}
rings += "\n\n";
}
return rings;
}

View file

@ -1,48 +1,24 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#ifndef TRANSACTIONINFO_H
#define TRANSACTIONINFO_H
#include <wallet/api/wallet2_api.h>
#include <QObject>
#include <QDateTime>
#include <QSet>
#ifndef FEATHER_TRANSACTIONROW_H
#define FEATHER_TRANSACTIONROW_H
class Transfer;
class Ring;
class TransactionInfo : public QObject
#include <QObject>
#include <QSet>
#include <QDateTime>
class TransactionRow : public QObject
{
Q_OBJECT
Q_PROPERTY(Direction direction READ direction)
Q_PROPERTY(bool isPending READ isPending)
Q_PROPERTY(bool isFailed READ isFailed)
Q_PROPERTY(bool isCoinbase READ isCoinbase)
Q_PROPERTY(double amount READ amount)
Q_PROPERTY(quint64 atomicAmount READ atomicAmount)
Q_PROPERTY(QString displayAmount READ displayAmount)
Q_PROPERTY(QString fee READ fee)
Q_PROPERTY(quint64 blockHeight READ blockHeight)
Q_PROPERTY(QString description READ description)
Q_PROPERTY(QSet<quint32> subaddrIndex READ subaddrIndex)
Q_PROPERTY(quint32 subaddrAccount READ subaddrAccount)
Q_PROPERTY(QString label READ label)
Q_PROPERTY(quint64 confirmations READ confirmations)
Q_PROPERTY(quint64 confirmationsRequired READ confirmationsRequired)
Q_PROPERTY(quint64 unlockTime READ unlockTime)
Q_PROPERTY(QString hash READ hash)
Q_PROPERTY(QDateTime timestamp READ timestamp)
Q_PROPERTY(QString date READ date)
Q_PROPERTY(QString time READ time)
Q_PROPERTY(QString paymentId READ paymentId)
Q_PROPERTY(QString destinations_formatted READ destinations_formatted)
Q_PROPERTY(QString rings_formatted READ rings_formatted)
public:
enum Direction {
Direction_In = Monero::TransactionInfo::Direction_In,
Direction_Out = Monero::TransactionInfo::Direction_Out,
Direction_In = 0,
Direction_Out = 1,
Direction_Both // invalid direction value, used for filtering
};
@ -80,7 +56,10 @@ public:
QString rings_formatted() const;
private:
explicit TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr);
explicit TransactionRow();
// TransactionRow(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr);
private:
friend class TransactionHistory;
mutable QList<Transfer*> m_transfers;
@ -104,4 +83,5 @@ private:
bool m_coinbase;
};
#endif // TRANSACTIONINFO_H
#endif //FEATHER_TRANSACTIONROW_H

View file

@ -70,12 +70,12 @@ QVariant AddressBookModel::data(const QModelIndex &index, int role) const
{
QVariant result;
bool found = m_addressBook->getRow(index.row(), [this, &result, &role, &index](const AddressBookInfo &row) {
bool found = m_addressBook->getRow(index.row(), [this, &result, &role, &index](const ContactRow &row) {
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
switch (index.column()) {
case Address:
{
QString address = row.address();
QString address = row.getAddress();
if (!m_showFullAddresses && role != Qt::UserRole) {
address = Utils::displayAddress(address);
}
@ -83,7 +83,7 @@ QVariant AddressBookModel::data(const QModelIndex &index, int role) const
break;
}
case Description:
result = row.description();
result = row.getLabel();
break;
default:
qCritical() << "Invalid column" << index.column();

View file

@ -2,7 +2,7 @@
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "CoinsModel.h"
#include "CoinsInfo.h"
#include "libwalletqt/rows/CoinsInfo.h"
#include "Coins.h"
#include "constants.h"
#include "utils/ColorScheme.h"

View file

@ -3,7 +3,7 @@
#include "CoinsProxyModel.h"
#include "CoinsModel.h"
#include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/rows/CoinsInfo.h"
CoinsProxyModel::CoinsProxyModel(QObject *parent, Coins *coins)
: QSortFilterProxyModel(parent)

View file

@ -4,7 +4,6 @@
#include "HistoryView.h"
#include "TransactionHistoryProxyModel.h"
#include "libwalletqt/TransactionInfo.h"
#include "utils/Utils.h"
#include <QHeaderView>
@ -71,7 +70,7 @@ TransactionHistoryModel* HistoryView::sourceModel()
return dynamic_cast<TransactionHistoryModel *>(m_model->sourceModel());
}
TransactionInfo* HistoryView::currentEntry()
TransactionRow* HistoryView::currentEntry()
{
QModelIndexList list = selectionModel()->selectedRows();
if (list.size() == 1) {
@ -199,7 +198,7 @@ void HistoryView::resetViewToDefaults()
}
void HistoryView::keyPressEvent(QKeyEvent *event) {
TransactionInfo* tx = this->currentEntry();
TransactionRow* tx = this->currentEntry();
if (event->matches(QKeySequence::Copy) && tx) {
Utils::copyToClipboard(tx->hash());

View file

@ -18,7 +18,7 @@ class HistoryView : public QTreeView
public:
explicit HistoryView(QWidget* parent = nullptr);
void setHistoryModel(TransactionHistoryProxyModel *model);
TransactionInfo* currentEntry();
TransactionRow* currentEntry();
void setSearchMode(bool mode);
QByteArray viewState() const;

View file

@ -8,6 +8,7 @@
#include "utils/Utils.h"
#include "libwalletqt/WalletManager.h"
#include "rows/AccountRow.h"
SubaddressAccountModel::SubaddressAccountModel(QObject *parent, SubaddressAccount *subaddressAccount)
: QAbstractTableModel(parent)
@ -49,7 +50,7 @@ QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const
QVariant result;
bool found = m_subaddressAccount->getRow(index.row(), [this, &index, &result, &role](const Monero::SubaddressAccountRow &row) {
bool found = m_subaddressAccount->getRow(index.row(), [this, &index, &result, &role](const AccountRow &row) {
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
result = parseSubaddressAccountRow(row, index, role);
}
@ -72,7 +73,7 @@ QVariant SubaddressAccountModel::data(const QModelIndex &index, int role) const
return result;
}
QVariant SubaddressAccountModel::parseSubaddressAccountRow(const Monero::SubaddressAccountRow &row,
QVariant SubaddressAccountModel::parseSubaddressAccountRow(const AccountRow &row,
const QModelIndex &index, int role) const
{
switch (index.column()) {
@ -82,19 +83,19 @@ QVariant SubaddressAccountModel::parseSubaddressAccountRow(const Monero::Subaddr
}
return QString("#%1").arg(QString::number(index.row()));
case Address:
return QString::fromStdString(row.getAddress());
return row.getAddress();
case Label:
return QString::fromStdString(row.getLabel());
return row.getLabel();
case Balance:
if (role == Qt::UserRole) {
return WalletManager::amountFromString(QString::fromStdString(row.getBalance()));
return WalletManager::amountFromString(row.getBalance());
}
return QString::fromStdString(row.getBalance());
return row.getBalance();
case UnlockedBalance:
if (role == Qt::UserRole) {
return WalletManager::amountFromString(QString::fromStdString(row.getUnlockedBalance()));
return WalletManager::amountFromString(row.getUnlockedBalance());
}
return QString::fromStdString(row.getUnlockedBalance());
return row.getUnlockedBalance();
default:
return QVariant();
}
@ -154,8 +155,7 @@ Qt::ItemFlags SubaddressAccountModel::flags(const QModelIndex &index) const
return QAbstractTableModel::flags(index);
}
Monero::SubaddressAccountRow* SubaddressAccountModel::entryFromIndex(const QModelIndex &index) const {
Q_ASSERT(index.isValid() && index.row() < m_subaddressAccount->count());
AccountRow* SubaddressAccountModel::entryFromIndex(const QModelIndex &index) const {
return m_subaddressAccount->row(index.row());
}

View file

@ -8,6 +8,8 @@
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
#include "rows/AccountRow.h"
class SubaddressAccount;
class SubaddressAccountModel : public QAbstractTableModel
@ -34,14 +36,14 @@ public:
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Monero::SubaddressAccountRow* entryFromIndex(const QModelIndex &index) const;
AccountRow* entryFromIndex(const QModelIndex &index) const;
public slots:
void startReset();
void endReset();
private:
QVariant parseSubaddressAccountRow(const Monero::SubaddressAccountRow &row, const QModelIndex &index, int role) const;
QVariant parseSubaddressAccountRow(const AccountRow &row, const QModelIndex &index, int role) const;
SubaddressAccount *m_subaddressAccount;
};

View file

@ -3,13 +3,13 @@
#include "TransactionHistoryModel.h"
#include "TransactionHistory.h"
#include "TransactionInfo.h"
#include "constants.h"
#include "utils/config.h"
#include "utils/ColorScheme.h"
#include "utils/Icons.h"
#include "utils/AppData.h"
#include "utils/Utils.h"
#include "libwalletqt/rows/TransactionRow.h"
TransactionHistoryModel::TransactionHistoryModel(QObject *parent)
: QAbstractTableModel(parent),
@ -34,7 +34,7 @@ TransactionHistory *TransactionHistoryModel::transactionHistory() const {
return m_transactionHistory;
}
TransactionInfo* TransactionHistoryModel::entryFromIndex(const QModelIndex &index) const {
TransactionRow* TransactionHistoryModel::entryFromIndex(const QModelIndex &index) const {
Q_ASSERT(index.isValid() && index.row() < m_transactionHistory->count());
return m_transactionHistory->transaction(index.row());
}
@ -65,7 +65,7 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
QVariant result;
bool found = m_transactionHistory->transaction(index.row(), [this, &index, &result, &role](const TransactionInfo &tInfo) {
bool found = m_transactionHistory->transaction(index.row(), [this, &index, &result, &role](const TransactionRow &tInfo) {
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
result = parseTransactionInfo(tInfo, index.column(), role);
}
@ -117,7 +117,7 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
case Column::FiatAmount:
case Column::Amount:
{
if (tInfo.direction() == TransactionInfo::Direction_Out) {
if (tInfo.direction() == TransactionRow::Direction_Out) {
result = QVariant(QColor("#BC1E1E"));
}
}
@ -139,7 +139,7 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
return result;
}
QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const
QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionRow &tInfo, int column, int role) const
{
switch (column)
{
@ -159,7 +159,7 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
return tInfo.balanceDelta();
}
QString amount = QString::number(tInfo.balanceDelta() / constants::cdiv, 'f', conf()->get(Config::amountPrecision).toInt());
amount = (tInfo.direction() == TransactionInfo::Direction_Out) ? "-" + amount : "+" + amount;
amount = (tInfo.direction() == TransactionRow::Direction_Out) ? "-" + amount : "+" + amount;
return amount;
}
case Column::TxID: {
@ -227,7 +227,7 @@ bool TransactionHistoryModel::setData(const QModelIndex &index, const QVariant &
switch (index.column()) {
case Column::Description:
{
m_transactionHistory->transaction(index.row(), [this, &hash, &value](const TransactionInfo &tInfo){
m_transactionHistory->transaction(index.row(), [this, &hash, &value](const TransactionRow &tInfo){
hash = tInfo.hash();
});
m_transactionHistory->setTxNote(hash, value.toString());

View file

@ -8,7 +8,7 @@
#include <QIcon>
class TransactionHistory;
class TransactionInfo;
class TransactionRow;
/**
* @brief The TransactionHistoryModel class - read-only table model for Transaction History
@ -33,7 +33,7 @@ public:
explicit TransactionHistoryModel(QObject * parent = nullptr);
void setTransactionHistory(TransactionHistory * th);
TransactionHistory * transactionHistory() const;
TransactionInfo* entryFromIndex(const QModelIndex& index) const;
TransactionRow* entryFromIndex(const QModelIndex& index) const;
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
@ -47,7 +47,7 @@ signals:
void transactionHistoryChanged();
private:
QVariant parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const;
QVariant parseTransactionInfo(const TransactionRow &tInfo, int column, int role) const;
TransactionHistory * m_transactionHistory;
};

View file

@ -4,7 +4,7 @@
#include "TransactionHistoryProxyModel.h"
#include "TransactionHistoryModel.h"
#include "libwalletqt/TransactionInfo.h"
#include "libwalletqt/rows/TransactionRow.h"
TransactionHistoryProxyModel::TransactionHistoryProxyModel(Wallet *wallet, QObject *parent)
: QSortFilterProxyModel(parent)
@ -25,7 +25,7 @@ bool TransactionHistoryProxyModel::filterAcceptsRow(int sourceRow, const QModelI
quint32 subaddrAccount;
QSet<quint32> subaddrIndex;
m_history->transaction(sourceRow, [&description, &txid, &subaddrlabel, &subaddrAccount, &subaddrIndex](TransactionInfo &tInfo){
m_history->transaction(sourceRow, [&description, &txid, &subaddrlabel, &subaddrAccount, &subaddrIndex](TransactionRow &tInfo){
description = tInfo.description();
txid = tInfo.hash();
subaddrlabel = tInfo.label();