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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,48 +4,54 @@
#include "AddressBook.h" #include "AddressBook.h"
#include <QDebug> #include <QDebug>
AddressBook::AddressBook(Monero::AddressBook *abImpl, QObject *parent) AddressBook::AddressBook(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
: QObject(parent), m_addressBookImpl(abImpl) : QObject(parent)
, m_wallet(wallet)
, m_wallet2(wallet2)
{ {
getAll(); this->refresh();
} }
QString AddressBook::errorString() const 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(); emit refreshStarted();
{ clearRows();
QWriteLocker locker(&m_lock);
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(); std::string address;
m_rows.clear(); 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()) { auto* abr = new ContactRow{this,
m_addresses.insert(QString::fromStdString(abr->getAddress()), m_rows.size()); i,
QString::fromStdString(address),
m_rows.append(new AddressBookInfo(abr, this)); QString::fromStdString(row->m_description)};
} m_rows.push_back(abr);
} }
emit refreshFinished(); 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()) if (index < 0 || index >= m_rows.size())
{ {
return false; return false;
@ -55,88 +61,58 @@ bool AddressBook::getRow(int index, std::function<void (AddressBookInfo &)> call
return true; 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; m_errorString = "";
bool result;
{ cryptonote::address_parse_info info;
QWriteLocker locker(&m_lock); if (!cryptonote::get_account_address_from_str(info, m_wallet2->nettype(), address.toStdString())) {
m_errorString = tr("Invalid destination address");
result = m_addressBookImpl->addRow(address.toStdString(), payment_id.toStdString(), description.toStdString()); m_errorCode = Invalid_Address;
return false;
} }
if (result) 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)
getAll(); refresh();
} else
m_errorCode = General_Error;
return result; return r;
} }
void AddressBook::setDescription(int index, const QString &description) { bool AddressBook::setDescription(int index, const QString &description) {
bool result; m_errorString = "";
{ const auto ab = m_wallet2->get_address_book();
QWriteLocker locker(&m_lock); if (index >= ab.size()){
return false;
result = m_addressBookImpl->setDescription(index, description.toStdString());
} }
if (result) tools::wallet2::address_book_row entry = ab[index];
{ entry.m_description = description.toStdString();
getAll(); 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);
emit descriptionChanged(); if (r)
} refresh();
else
m_errorCode = General_Error;
return r;
} }
bool AddressBook::deleteRow(int rowId) bool AddressBook::deleteRow(int rowId)
{ {
bool result; bool r = m_wallet2->delete_address_book_row(rowId);
if (r)
{ refresh();
QWriteLocker locker(&m_lock); return r;
result = m_addressBookImpl->deleteRow(rowId);
}
// Fetch new data from wallet2.
if (result)
{
getAll();
}
return result;
} }
quint64 AddressBook::count() const qsizetype AddressBook::count() const
{ {
QReadLocker locker(&m_lock); return m_rows.length();
return m_rows.size();
} }
QString AddressBook::getDescription(const QString &address) const void AddressBook::clearRows()
{ {
QReadLocker locker(&m_lock); qDeleteAll(m_rows);
m_rows.clear();
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();
} }

View file

@ -5,43 +5,44 @@
#define ADDRESSBOOK_H #define ADDRESSBOOK_H
#include <wallet/api/wallet2_api.h> #include <wallet/api/wallet2_api.h>
#include "AddressBookInfo.h"
#include <QMap> #include <QMap>
#include <QObject> #include <QObject>
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QList> #include <QList>
#include <QDateTime> #include <QDateTime>
#include "rows/ContactRow.h"
#include "Wallet.h"
#include "wallet/wallet2.h"
namespace Monero { namespace Monero {
struct AddressBook; struct AddressBook;
} }
class AddressBookRow;
class AddressBook : public QObject class AddressBook : public QObject
{ {
Q_OBJECT 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 { enum ErrorCode {
Status_Ok, Status_Ok,
General_Error, General_Error,
Invalid_Address, Invalid_Address,
Invalid_Payment_Id Invalid_Payment_Id
}; };
Q_ENUM(ErrorCode); Q_ENUM(ErrorCode);
private: bool getRow(int index, std::function<void (ContactRow &)> callback) const;
void getAll(); 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: signals:
void refreshStarted() const; void refreshStarted() const;
@ -49,12 +50,15 @@ signals:
void descriptionChanged() const; void descriptionChanged() const;
private: private:
explicit AddressBook(Monero::AddressBook * abImpl, QObject *parent); explicit AddressBook(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent);
friend class Wallet; friend class Wallet;
Monero::AddressBook * m_addressBookImpl;
mutable QReadWriteLock m_lock; Wallet *m_wallet;
QList<AddressBookInfo*> m_rows; tools::wallet2 *m_wallet2;
QMap<QString, size_t> m_addresses; QList<ContactRow*> m_rows;
QString m_errorString;
ErrorCode m_errorCode;
}; };
#endif // ADDRESSBOOK_H #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 // SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "Coins.h" #include "Coins.h"
#include "rows/CoinsInfo.h"
#include <QDebug> Coins::Coins(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
: QObject(parent)
#include "CoinsInfo.h" , m_wallet(wallet)
, m_wallet2(wallet2)
#include <QFile> {
}
bool Coins::coin(int index, std::function<void (CoinsInfo &)> callback) bool Coins::coin(int index, std::function<void (CoinsInfo &)> callback)
{ {
QReadLocker locker(&m_lock); 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: 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; return false;
} }
callback(*m_tinfo.value(index)); callback(*m_rows.value(index));
return true; return true;
} }
CoinsInfo* Coins::coin(int index) 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(); emit refreshStarted();
boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet2->m_transfers_mutex);
{ {
QWriteLocker locker(&m_lock); QWriteLocker locker(&m_lock);
qDeleteAll(m_tinfo); clearRows();
m_tinfo.clear(); uint32_t account = m_wallet->currentSubaddressAccount();
m_pimpl->refresh(); for (size_t i = 0; i < m_wallet2->get_num_transfer_details(); ++i)
for (const auto i : m_pimpl->getAll()) { {
if (i->subaddrAccount() != accountIndex) { const tools::wallet2::transfer_details &td = m_wallet2->get_transfer_details(i);
if (td.m_subaddr_index.major != account) {
continue; 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); QWriteLocker locker(&m_lock);
for (CoinsInfo* c : m_tinfo) { for (CoinsInfo* c : m_rows) {
if (!c->unlocked()) { 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); c->setUnlocked(unlocked);
} }
} }
@ -68,18 +98,50 @@ quint64 Coins::count() const
{ {
QReadLocker locker(&m_lock); 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(); 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(); emit coinThawed();
} }
@ -111,14 +173,12 @@ QVector<CoinsInfo*> Coins::coinsFromKeyImage(const QStringList &keyimages) {
void Coins::setDescription(const QString &publicKey, quint32 accountIndex, const QString &description) void Coins::setDescription(const QString &publicKey, quint32 accountIndex, const QString &description)
{ {
m_pimpl->setDescription(publicKey.toStdString(), description.toStdString()); m_wallet->setCacheAttribute(QString("coin.description:%1").arg(publicKey), description);
this->refresh(accountIndex); this->refresh();
emit descriptionChanged(); emit descriptionChanged();
} }
Coins::Coins(Monero::Coins *pimpl, QObject *parent) void Coins::clearRows() {
: QObject(parent) qDeleteAll(m_rows);
, m_pimpl(pimpl) m_rows.clear();
{
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,48 +2,49 @@
// SPDX-FileCopyrightText: 2020-2023 The Monero Project // SPDX-FileCopyrightText: 2020-2023 The Monero Project
#include "TransactionHistory.h" #include "TransactionHistory.h"
#include "TransactionInfo.h"
#include "utils/Utils.h" #include "utils/Utils.h"
#include "utils/AppData.h" #include "utils/AppData.h"
#include "utils/config.h" #include "utils/config.h"
#include "constants.h" #include "constants.h"
#include "WalletManager.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); 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: 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; return false;
} }
callback(*m_tinfo.value(index)); callback(*m_rows.value(index));
return true; return true;
} }
TransactionInfo* TransactionHistory::transaction(const QString &id) TransactionRow* TransactionHistory::transaction(const QString &id)
{ {
QReadLocker locker(&m_lock); QReadLocker locker(&m_lock);
auto itr = std::find_if(m_tinfo.begin(), m_tinfo.end(), auto itr = std::find_if(m_rows.begin(), m_rows.end(),
[&](const TransactionInfo * ti) { [&](const TransactionRow * ti) {
return ti->hash() == id; 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 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 firstDateTime = QDate(2014, 4, 18).startOfDay();
QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones) 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); QWriteLocker locker(&m_lock);
qDeleteAll(m_tinfo); clearRows();
m_tinfo.clear();
quint64 lastTxHeight = 0; quint64 lastTxHeight = 0;
m_locked = false; m_locked = false;
m_minutesToUnlock = 0; m_minutesToUnlock = 0;
m_pimpl->refresh();
for (const auto i : m_pimpl->getAll()) { uint64_t min_height = 0;
if (i->subaddrAccount() != accountIndex) { 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; 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(); auto* t = new TransactionRow();
// looking for transactions timestamp scope t->m_paymentId = QString::fromStdString(payment_id);
if (ti->timestamp() >= lastDateTime) { t->m_coinbase = pd.m_coinbase;
lastDateTime = ti->timestamp(); 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 uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
if (ti->confirmations() < requiredConfirmations && ti->blockHeight() >= lastTxHeight) { uint64_t fee = pd.m_amount_in - pd.m_amount_out;
lastTxHeight = ti->blockHeight();
// TODO: Fetch block time and confirmations needed from wallet2?
m_minutesToUnlock = (requiredConfirmations - ti->confirmations()) * 2; std::string payment_id = epee::string_tools::pod_to_hex(i->second.m_payment_id);
m_locked = true; 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(); 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) 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(); emit txNoteChanged();
} }
@ -109,7 +269,7 @@ quint64 TransactionHistory::count() const
{ {
QReadLocker locker(&m_lock); QReadLocker locker(&m_lock);
return m_tinfo.count(); return m_rows.length();
} }
QDateTime TransactionHistory::firstDateTime() const QDateTime TransactionHistory::firstDateTime() const
@ -133,8 +293,12 @@ bool TransactionHistory::TransactionHistory::locked() const
} }
TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent) TransactionHistory::TransactionHistory(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent)
: QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false) : QObject(parent)
, m_wallet(wallet)
, m_wallet2(wallet2)
, m_minutesToUnlock(0)
, m_locked(false)
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
m_firstDateTime = QDate(2014, 4, 18).startOfDay(); 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) 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) { bool TransactionHistory::writeCSV(const QString &path) {
QString data; QString data;
QReadLocker locker(&m_lock); 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(); return info1->blockHeight() < info2->blockHeight();
}); });
for (const auto &tx : transactions) { for (const auto &info : transactions) {
TransactionInfo info(tx, this);
// collect column data // collect column data
QDateTime timeStamp = info.timestamp(); QDateTime timeStamp = info->timestamp();
double amount = info.amount(); double amount = info->amount();
// calc historical fiat price // calc historical fiat price
QString fiatAmount; QString fiatAmount;
@ -176,31 +343,31 @@ bool TransactionHistory::writeCSV(const QString &path) {
fiatAmount = "\"?\""; fiatAmount = "\"?\"";
QString direction = QString(""); QString direction = QString("");
if (info.direction() == TransactionInfo::Direction_In) if (info->direction() == TransactionRow::Direction_In)
direction = QString("in"); direction = QString("in");
else if (info.direction() == TransactionInfo::Direction_Out) else if (info->direction() == TransactionRow::Direction_Out)
direction = QString("out"); direction = QString("out");
else else
continue; // skip TransactionInfo::Direction_Both continue; // skip TransactionInfo::Direction_Both
QString displayAmount = info.displayAmount(); QString displayAmount = info->displayAmount();
QString paymentId = info.paymentId(); QString paymentId = info->paymentId();
if (paymentId == "0000000000000000") { if (paymentId == "0000000000000000") {
paymentId = ""; 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()); QString balanceDelta = WalletManager::displayAmount(info->balanceDelta());
if (info.direction() == TransactionInfo::Direction_Out) { if (info->direction() == TransactionRow::Direction_Out) {
balanceDelta = "-" + balanceDelta; balanceDelta = "-" + balanceDelta;
} }
// format and write // format and write
QString line = QString("\n%1,%2,\"%3\",%4,\"%5\",%6,%7,%8,\"%9\",\"%10\",\"%11\",%12,\"%13\"") 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, .arg(QString::number(info->blockHeight()), QString::number(timeStamp.toSecsSinceEpoch()), date,
QString::number(info.subaddrAccount()), direction, balanceDelta, info.displayAmount(), QString::number(info->subaddrAccount()), direction, balanceDelta, info->displayAmount(),
info.fee(), info.hash(), info.description(), paymentId, fiatAmount, preferredFiatSymbol); info->fee(), info->hash(), info->description(), paymentId, fiatAmount, preferredFiatSymbol);
data += line; data += line;
} }

View file

@ -11,6 +11,10 @@
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QDateTime> #include <QDateTime>
#include "rows/TransactionRow.h"
#include "Wallet.h"
#include "wallet/wallet2.h"
namespace Monero { namespace Monero {
struct TransactionHistory; struct TransactionHistory;
} }
@ -20,24 +24,20 @@ class TransactionInfo;
class TransactionHistory : public QObject class TransactionHistory : public QObject
{ {
Q_OBJECT 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: public:
Q_INVOKABLE bool transaction(int index, std::function<void (TransactionInfo &)> callback); bool transaction(int index, std::function<void (TransactionRow &)> callback);
Q_INVOKABLE TransactionInfo * transaction(const QString &id); TransactionRow * transaction(const QString &id);
TransactionInfo* transaction(int index); TransactionRow* transaction(int index);
Q_INVOKABLE void refresh(quint32 accountIndex); void refresh();
Q_INVOKABLE void setTxNote(const QString &txid, const QString &note); void setTxNote(const QString &txid, const QString &note);
Q_INVOKABLE bool writeCSV(const QString &path); bool writeCSV(const QString &path);
quint64 count() const; quint64 count() const;
QDateTime firstDateTime() const; QDateTime firstDateTime() const;
QDateTime lastDateTime() const; QDateTime lastDateTime() const;
quint64 minutesToUnlock() const; quint64 minutesToUnlock() const;
bool locked() const; bool locked() const;
void clearRows();
signals: signals:
void refreshStarted() const; void refreshStarted() const;
@ -47,13 +47,16 @@ signals:
void txNoteChanged() const; void txNoteChanged() const;
private: private:
explicit TransactionHistory(Monero::TransactionHistory * pimpl, QObject *parent = nullptr); explicit TransactionHistory(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent = nullptr);
private: private:
friend class Wallet; friend class Wallet;
mutable QReadWriteLock m_lock; 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_firstDateTime;
mutable QDateTime m_lastDateTime; mutable QDateTime m_lastDateTime;
mutable int m_minutesToUnlock; 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 Q_OBJECT
private: public:
explicit Transfer(uint64_t _amount, QString _address, QObject *parent = 0) explicit Transfer(uint64_t _amount, QString _address, QObject *parent = 0)
: QObject(parent), m_amount(_amount), m_address(std::move(_address)) {}; : QObject(parent), m_amount(_amount), m_address(std::move(_address)) {};
private: private:

View file

@ -32,21 +32,21 @@ namespace {
Wallet::Wallet(Monero::Wallet *wallet, QObject *parent) Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
: QObject(parent) : QObject(parent)
, m_walletImpl(wallet) , m_walletImpl(wallet)
, m_history(new TransactionHistory(m_walletImpl->history(), this)) , m_history(new TransactionHistory(this, wallet->getWallet(), this))
, m_historyModel(nullptr) , m_historyModel(nullptr)
, m_addressBook(new AddressBook(m_walletImpl->addressBook(), this)) , m_addressBook(new AddressBook(this, wallet->getWallet(), this))
, m_addressBookModel(nullptr) , m_addressBookModel(nullptr)
, m_daemonBlockChainHeight(0) , m_daemonBlockChainHeight(0)
, m_daemonBlockChainTargetHeight(0) , m_daemonBlockChainTargetHeight(0)
, m_connectionStatus(Wallet::ConnectionStatus_Disconnected) , m_connectionStatus(Wallet::ConnectionStatus_Disconnected)
, m_currentSubaddressAccount(0) , m_currentSubaddressAccount(0)
, m_subaddress(new Subaddress(this, wallet->getWallet(), this)) , 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_refreshNow(false)
, m_refreshEnabled(false) , m_refreshEnabled(false)
, m_scheduler(this) , m_scheduler(this)
, m_useSSL(true) , 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_storeTimer(new QTimer(this))
{ {
m_walletListener = new WalletListenerImpl(this); m_walletListener = new WalletListenerImpl(this);
@ -71,7 +71,7 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
} }
connect(this->history(), &TransactionHistory::txNoteChanged, [this]{ connect(this->history(), &TransactionHistory::txNoteChanged, [this]{
this->history()->refresh(this->currentSubaddressAccount()); this->history()->refresh();
}); });
connect(this, &Wallet::refreshed, this, &Wallet::onRefreshed); 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"; qWarning() << "failed to set " << ATTRIBUTE_SUBADDRESS_ACCOUNT << " cache attribute";
} }
m_subaddress->refresh(m_currentSubaddressAccount); m_subaddress->refresh(m_currentSubaddressAccount);
m_history->refresh(m_currentSubaddressAccount); m_history->refresh();
m_coins->refresh(m_currentSubaddressAccount); m_coins->refresh();
this->subaddressModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); this->subaddressModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount);
this->coinsModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount); this->coinsModel()->setCurrentSubaddressAccount(m_currentSubaddressAccount);
this->updateBalance(); this->updateBalance();
@ -453,8 +453,8 @@ void Wallet::onRefreshed(bool success, const QString &message) {
} }
void Wallet::refreshModels() { void Wallet::refreshModels() {
m_history->refresh(this->currentSubaddressAccount()); m_history->refresh();
m_coins->refresh(this->currentSubaddressAccount()); m_coins->refresh();
bool r = this->subaddress()->refresh(this->currentSubaddressAccount()); bool r = this->subaddress()->refresh(this->currentSubaddressAccount());
if (!r) { 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 // Store wallet immediately, so we don't risk losing tx key if wallet crashes
this->storeSafer(); this->storeSafer();
this->history()->refresh(this->currentSubaddressAccount()); this->history()->refresh();
this->coins()->refresh(this->currentSubaddressAccount()); this->coins()->refresh();
this->updateBalance(); this->updateBalance();
if (!success) { 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 { QString CoinsInfo::addressLabel() const {
if (m_subaddrIndex == 0) { 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; return m_addressLabel;
@ -108,29 +116,28 @@ QString CoinsInfo::description() const {
return m_description; return m_description;
} }
CoinsInfo::CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent) bool CoinsInfo::change() const {
return m_change;
}
CoinsInfo::CoinsInfo(QObject *parent)
: QObject(parent) : QObject(parent)
, m_blockHeight(pimpl->blockHeight()) , m_blockHeight(0)
, m_hash(QString::fromStdString(pimpl->hash())) , m_internalOutputIndex(0)
, m_internalOutputIndex(pimpl->internalOutputIndex()) , m_globalOutputIndex(0)
, m_globalOutputIndex(pimpl->globalOutputIndex()) , m_spent(false)
, m_spent(pimpl->spent()) , m_frozen(false)
, m_frozen(pimpl->frozen()) , m_spentHeight(0)
, m_spentHeight(pimpl->spentHeight()) , m_amount(0)
, m_amount(pimpl->amount()) , m_rct(false)
, m_rct(pimpl->rct()) , m_keyImageKnown(false)
, m_keyImageKnown(pimpl->keyImageKnown()) , m_pkIndex(0)
, m_pkIndex(pimpl->pkIndex()) , m_subaddrIndex(0)
, m_subaddrIndex(pimpl->subaddrIndex()) , m_subaddrAccount(0)
, m_subaddrAccount(pimpl->subaddrAccount()) , m_unlockTime(0)
, m_address(QString::fromStdString(pimpl->address())) , m_unlocked(false)
, m_addressLabel(QString::fromStdString(pimpl->addressLabel())) , m_coinbase(false)
, m_keyImage(QString::fromStdString(pimpl->keyImage())) , m_change(false)
, m_unlockTime(pimpl->unlockTime())
, m_unlocked(pimpl->unlocked())
, m_pubKey(QString::fromStdString(pimpl->pubKey()))
, m_coinbase(pimpl->coinbase())
, m_description(QString::fromStdString(pimpl->description()))
{ {
} }

View file

@ -14,28 +14,6 @@ class Coins;
class CoinsInfo : public QObject class CoinsInfo : public QObject
{ {
Q_OBJECT 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: public:
quint64 blockHeight() const; quint64 blockHeight() const;
@ -60,11 +38,13 @@ public:
QString pubKey() const; QString pubKey() const;
bool coinbase() const; bool coinbase() const;
QString description() const; QString description() const;
bool change() const;
void setUnlocked(bool unlocked); void setUnlocked(bool unlocked);
private: private:
explicit CoinsInfo(const Monero::CoinsInfo *pimpl, QObject *parent = nullptr); explicit CoinsInfo(QObject *parent);
private: private:
friend class Coins; friend class Coins;
@ -89,6 +69,7 @@ private:
QString m_pubKey; QString m_pubKey;
bool m_coinbase; bool m_coinbase;
QString m_description; QString m_description;
bool m_change;
}; };
#endif //FEATHER_COINSINFO_H #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-License-Identifier: BSD-3-Clause
// SPDX-FileCopyrightText: 2020-2023 The Monero Project // SPDX-FileCopyrightText: 2020-2023 The Monero Project
#ifndef TRANSACTIONINFO_H #ifndef FEATHER_TRANSACTIONROW_H
#define TRANSACTIONINFO_H #define FEATHER_TRANSACTIONROW_H
#include <wallet/api/wallet2_api.h>
#include <QObject>
#include <QDateTime>
#include <QSet>
class Transfer; class Transfer;
class Ring; class Ring;
class TransactionInfo : public QObject #include <QObject>
#include <QSet>
#include <QDateTime>
class TransactionRow : public QObject
{ {
Q_OBJECT 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: public:
enum Direction { enum Direction {
Direction_In = Monero::TransactionInfo::Direction_In, Direction_In = 0,
Direction_Out = Monero::TransactionInfo::Direction_Out, Direction_Out = 1,
Direction_Both // invalid direction value, used for filtering Direction_Both // invalid direction value, used for filtering
}; };
@ -80,7 +56,10 @@ public:
QString rings_formatted() const; QString rings_formatted() const;
private: private:
explicit TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr); explicit TransactionRow();
// TransactionRow(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr);
private: private:
friend class TransactionHistory; friend class TransactionHistory;
mutable QList<Transfer*> m_transfers; mutable QList<Transfer*> m_transfers;
@ -104,4 +83,5 @@ private:
bool m_coinbase; 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; 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) { if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
switch (index.column()) { switch (index.column()) {
case Address: case Address:
{ {
QString address = row.address(); QString address = row.getAddress();
if (!m_showFullAddresses && role != Qt::UserRole) { if (!m_showFullAddresses && role != Qt::UserRole) {
address = Utils::displayAddress(address); address = Utils::displayAddress(address);
} }
@ -83,7 +83,7 @@ QVariant AddressBookModel::data(const QModelIndex &index, int role) const
break; break;
} }
case Description: case Description:
result = row.description(); result = row.getLabel();
break; break;
default: default:
qCritical() << "Invalid column" << index.column(); qCritical() << "Invalid column" << index.column();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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