History: cleanup

This commit is contained in:
tobtoht 2025-03-12 10:04:33 +01:00
parent 1219bfc3a7
commit 4f28d83ac3
No known key found for this signature in database
GPG key ID: E45B10DD027D2472
28 changed files with 430 additions and 634 deletions

View file

@ -377,4 +377,4 @@ QModelIndex CoinsWidget::getCurrentIndex()
return m_proxyModel->mapToSource(list.first());
}
CoinsWidget::~CoinsWidget() = default;
CoinsWidget::~CoinsWidget() = default;

View file

@ -84,15 +84,18 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
QMenu menu(this);
auto *tx = ui->history->currentEntry();
if (!tx) return;
auto txIdx = ui->history->getCurrentIndex();
if (!txIdx.isValid()) {
return;
}
const TransactionRow& tx = ui->history->sourceModel()->entryFromIndex(txIdx);
bool unconfirmed = tx->isFailed() || tx->isPending();
if (unconfirmed && tx->direction() != TransactionRow::Direction_In) {
bool unconfirmed = tx.failed || tx.pending;
if (unconfirmed && tx.direction != TransactionRow::Direction_In) {
menu.addAction("Resend transaction", this, &HistoryWidget::onResendTransaction);
}
if (tx->isFailed()) {
if (tx.failed) {
menu.addAction("Remove from history", this, &HistoryWidget::onRemoveFromHistory);
}
@ -105,20 +108,24 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
}
void HistoryWidget::onResendTransaction() {
auto *tx = ui->history->currentEntry();
if (tx) {
QString txid = tx->hash();
emit resendTransaction(txid);
auto index = ui->history->getCurrentIndex();
if (!index.isValid()) {
return;
}
const TransactionRow& tx = ui->history->sourceModel()->entryFromIndex(index);
emit resendTransaction(tx.hash);
}
void HistoryWidget::onRemoveFromHistory() {
auto *tx = ui->history->currentEntry();
if (!tx) return;
auto index = ui->history->getCurrentIndex();
if (!index.isValid()) {
return;
}
const TransactionRow& tx = ui->history->sourceModel()->entryFromIndex(index);
auto result = QMessageBox::question(this, "Remove transaction from history", "Are you sure you want to remove this transaction from the history?");
if (result == QMessageBox::Yes) {
m_wallet->removeFailedTx(tx->hash());
m_wallet->removeFailedTx(tx.hash);
}
}
@ -132,8 +139,11 @@ void HistoryWidget::resetModel()
}
void HistoryWidget::showTxDetails() {
auto *tx = ui->history->currentEntry();
if (!tx) return;
auto index = ui->history->getCurrentIndex();
if (!index.isValid()) {
return;
}
const TransactionRow& tx = ui->history->sourceModel()->entryFromIndex(index);
auto *dialog = new TxInfoDialog(m_wallet, tx, this);
connect(dialog, &TxInfoDialog::resendTranscation, [this](const QString &txid){
@ -144,11 +154,13 @@ void HistoryWidget::showTxDetails() {
}
void HistoryWidget::onViewOnBlockExplorer() {
auto *tx = ui->history->currentEntry();
if (!tx) return;
auto index = ui->history->getCurrentIndex();
if (!index.isValid()) {
return;
}
const TransactionRow& tx = ui->history->sourceModel()->entryFromIndex(index);
QString txid = tx->hash();
emit viewOnBlockExplorer(txid);
emit viewOnBlockExplorer(tx.hash);
}
void HistoryWidget::setSearchText(const QString &text) {
@ -161,8 +173,11 @@ void HistoryWidget::setSearchFilter(const QString &filter) {
}
void HistoryWidget::createTxProof() {
auto *tx = ui->history->currentEntry();
if (!tx) return;
auto index = ui->history->getCurrentIndex();
if (!index.isValid()) {
return;
}
const TransactionRow& tx = ui->history->sourceModel()->entryFromIndex(index);
TxProofDialog dialog{this, m_wallet, tx};
dialog.getTxKey();
@ -170,20 +185,23 @@ void HistoryWidget::createTxProof() {
}
void HistoryWidget::copy(copyField field) {
auto *tx = ui->history->currentEntry();
if (!tx) return;
auto index = ui->history->getCurrentIndex();
if (!index.isValid()) {
return;
}
const TransactionRow& tx = ui->history->sourceModel()->entryFromIndex(index);
QString data = [field, tx]{
switch(field) {
case copyField::TxID:
return tx->hash();
return tx.hash;
case copyField::Description:
return tx->description();
return tx.description;
case copyField::Date:
return tx->timestamp().toString(QString("%1 %2").arg(conf()->get(Config::dateFormat).toString(),
return tx.timestamp.toString(QString("%1 %2").arg(conf()->get(Config::dateFormat).toString(),
conf()->get(Config::timeFormat).toString()));
case copyField::Amount:
return WalletManager::displayAmount(abs(tx->balanceDelta()));
return WalletManager::displayAmount(abs(tx.balanceDelta));
default:
return QString("");
}
@ -205,4 +223,4 @@ void HistoryWidget::showSyncNoticeMsg() {
"To update the history page during synchronization press Ctrl+R.");
}
HistoryWidget::~HistoryWidget() = default;
HistoryWidget::~HistoryWidget() = default;

View file

@ -942,7 +942,7 @@ void MainWindow::onTransactionCreated(PendingTransaction *tx, const QVector<QStr
// TODO: also check that amounts match
tx->refresh();
QSet<QString> outputAddresses;
for (const auto &output : tx->transaction(0)->outputs()) {
for (const auto &output : tx->transaction(0).outputs) {
outputAddresses.insert(WalletManager::baseAddressFromIntegratedAddress(output.address, constants::networkType));
}
QSet<QString> destAddresses;
@ -1053,8 +1053,17 @@ void MainWindow::onTransactionCommitted(bool success, PendingTransaction *tx, co
msgBox.exec();
if (msgBox.clickedButton() == showDetailsButton) {
this->showHistoryTab();
TransactionRow *txInfo = m_wallet->history()->transaction(txid.first());
auto *dialog = new TxInfoDialog(m_wallet, txInfo, this);
const auto& rows = m_wallet->history()->getRows();
auto itr = std::find_if(rows.begin(), rows.end(),
[&](const TransactionRow& ti) {
return ti.hash == txid.first();
});
if (itr == rows.end()) {
return;
}
auto *dialog = new TxInfoDialog(m_wallet, *itr, this);
connect(dialog, &TxInfoDialog::resendTranscation, this, &MainWindow::onResendTransaction);
dialog->show();
dialog->setAttribute(Qt::WA_DeleteOnClose);

View file

@ -95,77 +95,77 @@ void HistoryExportDialog::exportHistory()
QList<QPair<uint64_t, QString>> csvData;
for (int i = 0; i < num_transactions; i++) {
TransactionRow* tx = m_wallet->history()->transaction(i);
const TransactionRow& tx = m_wallet->history()->transaction(i);
QDate minimumDate = ui->date_min->date();
if (tx->timestamp().date() < minimumDate) {
if (tx.timestamp.date() < minimumDate) {
continue;
}
QDate maximumDate = ui->date_max->date();
if (tx->timestamp().date() > maximumDate) {
if (tx.timestamp.date() > maximumDate) {
continue;
}
if (ui->check_excludePending->isChecked() && tx->isPending()) {
if (ui->check_excludePending->isChecked() && tx.pending) {
continue;
}
if (ui->check_excludeFailed->isChecked() && tx->isFailed()) {
if (ui->check_excludeFailed->isChecked() && tx.failed) {
continue;
}
if (ui->radio_incomingTransactions->isChecked() && tx->direction() != TransactionRow::Direction_In) {
if (ui->radio_incomingTransactions->isChecked() && tx.direction != TransactionRow::Direction_In) {
continue;
}
if (ui->radio_outgoingTransactions->isChecked() && tx->direction() != TransactionRow::Direction_Out) {
if (ui->radio_outgoingTransactions->isChecked() && tx.direction != TransactionRow::Direction_Out) {
continue;
}
if (ui->radio_coinbaseTransactions->isChecked() && !tx->isCoinbase()) {
if (ui->radio_coinbaseTransactions->isChecked() && !tx.coinbase) {
continue;
}
QString date = QString("%1T%2Z").arg(tx->date(), tx->time());
QString date = QString("%1T%2Z").arg(tx.date(), tx.time());
QString direction = QString("");
if (tx->direction() == TransactionRow::Direction_In)
if (tx.direction == TransactionRow::Direction_In)
direction = QString("in");
else if (tx->direction() == TransactionRow::Direction_Out)
else if (tx.direction == TransactionRow::Direction_Out)
direction = QString("out");
else
continue; // skip TransactionInfo::Direction_Both
QString balanceDelta = WalletManager::displayAmount(abs(tx->balanceDelta()));
if (tx->direction() == TransactionRow::Direction_Out) {
QString balanceDelta = WalletManager::displayAmount(abs(tx.balanceDelta));
if (tx.direction == TransactionRow::Direction_Out) {
balanceDelta = "-" + balanceDelta;
}
QString paymentId = tx->paymentId();
QString paymentId = tx.paymentId;
if (paymentId == "0000000000000000") {
paymentId = "";
}
const double usd_price = appData()->txFiatHistory->get(tx->timestamp().toString("yyyyMMdd"));
double fiat_price = usd_price * tx->amount();
const double usd_price = appData()->txFiatHistory->get(tx.timestamp.toString("yyyyMMdd"));
double fiat_price = usd_price * tx.amountDouble();
QString fiatAmount = (usd_price > 0) ? QString::number(fiat_price, 'f', 2) : "?";
QString line = QString(R"(%1,%2,"%3",%4,"%5",%6,%7,%8,"%9","%10","%11","%12","%13")")
.arg(QString::number(tx->blockHeight()),
QString::number(tx->timestamp().toSecsSinceEpoch()),
.arg(QString::number(tx.blockHeight),
QString::number(tx.timestamp.toSecsSinceEpoch()),
date,
QString::number(tx->subaddrAccount()),
QString::number(tx.subaddrAccount),
direction,
balanceDelta,
tx->displayAmount(),
tx->fee(),
tx->hash(),
tx->description(),
tx.displayAmount(),
tx.displayFee(),
tx.hash,
tx.description,
paymentId,
fiatAmount,
"USD");
csvData.append({tx->blockHeight(), line});
csvData.append({tx.blockHeight, line});
}
std::sort(csvData.begin(), csvData.end(), [](const QPair<uint64_t, QString> &tx1, const QPair<uint64_t, QString> &tx2){

View file

@ -72,10 +72,10 @@ void TxConfAdvDialog::setTransaction(PendingTransaction *tx, bool isSigned) {
m_tx = tx;
m_tx->refresh();
PendingTransactionInfo *ptx = m_tx->transaction(0); //Todo: support split transactions
const PendingTransactionInfo& ptx = m_tx->transaction(0); //Todo: support split transactions
// TODO: implement hasTxKey()
if (!m_wallet->isHwBacked() && m_tx->transaction(0)->txKey() == "0100000000000000000000000000000000000000000000000000000000000000") {
if (!m_wallet->isHwBacked() && m_tx->transaction(0).txKey == "0100000000000000000000000000000000000000000000000000000000000000") {
ui->btn_exportTxKey->hide();
}
@ -100,7 +100,7 @@ void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
this->setAmounts(utx->amount(0), utx->fee(0));
ConstructionInfo *ci = m_utx->constructionInfo(0);
const ConstructionInfo& ci = m_utx->constructionInfo(0);
this->setupConstructionData(ci);
}
@ -136,20 +136,20 @@ void TxConfAdvDialog::setAmounts(quint64 amount, quint64 fee) {
}
}
void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
for (const auto &in: ci->inputs()) {
void TxConfAdvDialog::setupConstructionData(const ConstructionInfo& ci) {
for (const auto &in: ci.inputs) {
auto *item = new QTreeWidgetItem(ui->treeInputs);
item->setText(0, in->pubKey());
item->setText(0, in.pubKey);
item->setFont(0, Utils::getMonospaceFont());
item->setText(1, WalletManager::displayAmount(in->amount()));
item->setText(1, WalletManager::displayAmount(in.amount));
}
ui->treeInputs->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->treeInputs->resizeColumnToContents(1);
ui->treeInputs->header()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->label_inputs->setText(QString("Inputs (%1)").arg(QString::number(ci->inputs().size())));
ui->label_inputs->setText(QString("Inputs (%1)").arg(QString::number(ci.inputs.size())));
for (const auto &out: ci->outputs()) {
for (const auto &out: ci.outputs) {
auto *item = new QTreeWidgetItem(ui->treeOutputs);
item->setText(0, out.address);
item->setText(1, WalletManager::displayAmount(out.amount));
@ -175,7 +175,7 @@ void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
ui->treeOutputs->resizeColumnToContents(1);
ui->treeOutputs->header()->setSectionResizeMode(0, QHeaderView::Stretch);
ui->label_outputs->setText(QString("Outputs (%1)").arg(QString::number(ci->outputs().size())));
ui->label_outputs->setText(QString("Outputs (%1)").arg(QString::number(ci.outputs.size())));
this->adjustSize();
}
@ -210,7 +210,7 @@ void TxConfAdvDialog::txKeyCopy() {
return;
}
Utils::copyToClipboard(m_tx->transaction(0)->txKey());
Utils::copyToClipboard(m_tx->transaction(0).txKey);
}
void TxConfAdvDialog::broadcastTransaction() {

View file

@ -30,7 +30,7 @@ public:
void setUnsignedTransaction(UnsignedTransaction *utx);
private:
void setupConstructionData(ConstructionInfo *ci);
void setupConstructionData(const ConstructionInfo& ci);
void signTransaction();
void broadcastTransaction();
void closeDialog();

View file

@ -17,11 +17,10 @@
#include "utils/Icons.h"
#include "utils/Utils.h"
TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *parent)
TxInfoDialog::TxInfoDialog(Wallet *wallet, const TransactionRow &txInfo, QWidget *parent)
: QDialog(parent)
, ui(new Ui::TxInfoDialog)
, m_wallet(wallet)
, m_txInfo(txInfo)
, m_txProofDialog(new TxProofDialog(this, wallet, txInfo))
{
ui->setupUi(this);
@ -30,7 +29,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *pare
ui->btn_viewOnBlockExplorer->setToolTip("View on block explorer");
connect(ui->btn_viewOnBlockExplorer, &QPushButton::clicked, this, &TxInfoDialog::viewOnBlockExplorer);
m_txid = txInfo->hash();
m_txid = txInfo.hash;
ui->label_txid->setText(m_txid);
connect(ui->btn_copyTxID, &QPushButton::clicked, this, &TxInfoDialog::copyTxID);
@ -41,7 +40,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *pare
this->setData(txInfo);
if ((txInfo->isFailed() || txInfo->isPending()) && txInfo->direction() != TransactionRow::Direction_In) {
if ((txInfo.failed || txInfo.pending) && txInfo.direction != TransactionRow::Direction_In) {
connect(ui->btn_rebroadcastTx, &QPushButton::pressed, [this]{
emit resendTranscation(m_txid);
});
@ -49,7 +48,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *pare
ui->btn_rebroadcastTx->hide();
}
if (txInfo->direction() == TransactionRow::Direction_In) {
if (txInfo.direction == TransactionRow::Direction_In) {
ui->btn_CopyTxKey->setDisabled(true);
ui->btn_CopyTxKey->setToolTip("No tx secret key available for incoming transactions.");
}
@ -74,7 +73,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *pare
QTextCursor cursor = ui->outputs->textCursor();
auto transfers = txInfo->transfers();
auto transfers = txInfo.transfers;
if (!transfers.isEmpty()) {
bool hasIntegrated = false;
@ -94,7 +93,7 @@ TxInfoDialog::TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *pare
this->adjustHeight(ui->outputs, transfers.size());
// Trezor saves a mangled payment ID.
if (m_wallet->isTrezor() && !hasIntegrated && txInfo->hasPaymentId()) {
if (m_wallet->isTrezor() && !hasIntegrated && txInfo.hasPaymentId()) {
ui->frame_destinationsWarning->setInfo(icons()->icon("warning"), "The address displayed here does not contain a payment ID. "
"If you are making a repeat payment to a service, "
"do not copy the address from here to prevent a loss of funds.");
@ -122,10 +121,10 @@ void TxInfoDialog::adjustHeight(QTextEdit *textEdit, qreal docHeight) {
textEdit->verticalScrollBar()->hide();
}
void TxInfoDialog::setData(TransactionRow *tx) {
QString blockHeight = QString::number(tx->blockHeight());
void TxInfoDialog::setData(const TransactionRow &tx) {
QString blockHeight = QString::number(tx.blockHeight);
if (tx->isFailed()) {
if (tx.failed) {
ui->label_status->setText("Status: Failed (node was unable to relay transaction)");
}
if (blockHeight == "0") {
@ -133,41 +132,47 @@ void TxInfoDialog::setData(TransactionRow *tx) {
}
else {
QString dateTimeFormat = QString("%1 %2").arg(conf()->get(Config::dateFormat).toString(), conf()->get(Config::timeFormat).toString());
QString date = tx->timestamp().toString(dateTimeFormat);
QString statusText = QString("Status: Included in block %1 (%2 confirmations) on %3").arg(blockHeight, QString::number(tx->confirmations()), date);
QString date = tx.timestamp.toString(dateTimeFormat);
QString statusText = QString("Status: Included in block %1 (%2 confirmations) on %3").arg(blockHeight, QString::number(tx.confirmations), date);
ui->label_status->setText(statusText);
}
if (tx->confirmationsRequired() > tx->confirmations()) {
bool mandatoryLock = tx->confirmationsRequired() == 10;
QString confsRequired = QString::number(tx->confirmationsRequired() - tx->confirmations());
if (tx.confirmationsRequired() > tx.confirmations) {
bool mandatoryLock = tx.confirmationsRequired() == 10;
QString confsRequired = QString::number(tx.confirmationsRequired() - tx.confirmations);
ui->label_lock->setText(QString("Lock: Outputs become spendable in %1 blocks (%2)").arg(confsRequired, mandatoryLock ? "consensus rule" : "specified by sender"));
} else {
ui->label_lock->setText("Lock: Outputs are spendable");
}
QString direction = tx->direction() == TransactionRow::Direction_In ? "received" : "sent";
ui->label_amount->setText(QString("Amount %1: %2 XMR").arg(direction, tx->displayAmount()));
QString direction = tx.direction == TransactionRow::Direction_In ? "received" : "sent";
ui->label_amount->setText(QString("Amount %1: %2 XMR").arg(direction, tx.displayAmount()));
QString fee;
if (tx->isCoinbase())
if (tx.coinbase)
fee = "Not applicable";
else if (tx->direction() == TransactionRow::Direction_In)
else if (tx.direction == TransactionRow::Direction_In)
fee = "Paid by sender";
else if (tx->fee().isEmpty())
else if (tx.displayFee().isEmpty())
fee = "N/A";
else
fee = QString("%1 XMR").arg(tx->fee());
fee = QString("%1 XMR").arg(tx.displayFee());
ui->label_fee->setText(QString("Fee: %1").arg(fee));
}
void TxInfoDialog::updateData() {
TransactionRow *tx = m_wallet->history()->transaction(m_txid);
if (!tx) return;
this->setData(tx);
const auto& rows = m_wallet->history()->getRows();
auto itr = std::find_if(rows.begin(), rows.end(),
[&](const TransactionRow& ti) {
return ti.hash == m_txid;
});
if (itr == rows.end()) {
return;
}
this->setData(*itr);
}
void TxInfoDialog::copyTxID() {
@ -207,4 +212,4 @@ void TxInfoDialog::viewOnBlockExplorer() {
Utils::externalLinkWarning(this, link);
}
TxInfoDialog::~TxInfoDialog() = default;
TxInfoDialog::~TxInfoDialog() = default;

View file

@ -21,7 +21,7 @@ class TxInfoDialog : public QDialog
Q_OBJECT
public:
explicit TxInfoDialog(Wallet *wallet, TransactionRow *txInfo, QWidget *parent = nullptr);
explicit TxInfoDialog(Wallet *wallet, const TransactionRow &txInfo, QWidget *parent = nullptr);
~TxInfoDialog() override;
signals:
@ -31,14 +31,13 @@ private:
void copyTxID();
void copyTxKey();
void createTxProof();
void setData(TransactionRow *tx);
void setData(const TransactionRow& tx);
void updateData();
void adjustHeight(QTextEdit *textEdit, qreal docHeight);
void viewOnBlockExplorer();
QScopedPointer<Ui::TxInfoDialog> ui;
Wallet *m_wallet;
TransactionRow *m_txInfo;
TxProofDialog *m_txProofDialog;
QString m_txid;
};

View file

@ -10,23 +10,23 @@
#include "utils/Icons.h"
#include "utils/Utils.h"
TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, TransactionRow *txInfo)
TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, const TransactionRow& txInfo)
: WindowModalDialog(parent)
, ui(new Ui::TxProofDialog)
, m_wallet(wallet)
{
ui->setupUi(this);
m_txid = txInfo->hash();
m_txid = txInfo.hash;
m_direction = txInfo->direction();
m_direction = txInfo.direction;
for (auto const &t: txInfo->transfers()) {
for (auto const &t: txInfo.transfers) {
m_OutDestinations.push_back(t.address);
}
for (auto const &s: txInfo->subaddrIndex()) {
m_InDestinations.push_back(m_wallet->address(txInfo->subaddrAccount(), s));
for (auto const &s: txInfo.subaddrIndex) {
m_InDestinations.push_back(m_wallet->address(txInfo.subaddrAccount, s));
}
// Due to some logic in core we can't create OutProofs
@ -244,4 +244,4 @@ TxProof TxProofDialog::getProof() {
return proof;
}
TxProofDialog::~TxProofDialog() = default;
TxProofDialog::~TxProofDialog() = default;

View file

@ -19,7 +19,7 @@ class TxProofDialog : public WindowModalDialog
Q_OBJECT
public:
explicit TxProofDialog(QWidget *parent, Wallet *wallet, TransactionRow *txid);
explicit TxProofDialog(QWidget *parent, Wallet *wallet, const TransactionRow &txid);
~TxProofDialog() override;
void setTxId(const QString &txid);
void getTxKey();

View file

@ -7,47 +7,21 @@
#include "Transfer.h"
#include <wallet/api/wallet2_api.h>
quint64 ConstructionInfo::unlockTime() const {
return m_unlockTime;
}
QSet<quint32> ConstructionInfo::subaddressIndices() const {
return m_subaddressIndices;
}
QVector<QString> ConstructionInfo::subaddresses() const {
return m_subaddresses;
}
quint64 ConstructionInfo::minMixinCount() const {
return m_minMixinCount;
}
QList<Input *> ConstructionInfo::inputs() const {
return m_inputs;
}
QList<Transfer> ConstructionInfo::outputs() const {
return m_outputs;
}
ConstructionInfo::ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl, QObject *parent)
: QObject(parent)
, m_unlockTime(pimpl->unlockTime())
, m_minMixinCount(pimpl->minMixinCount())
ConstructionInfo::ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl)
: unlockTime(pimpl->unlockTime())
, minMixinCount(pimpl->minMixinCount())
{
for (auto const &i : pimpl->inputs())
{
Input *input = new Input(i.amount, QString::fromStdString(i.pubkey), this);
m_inputs.append(input);
inputs.emplace_back(i.amount, QString::fromStdString(i.pubkey));
}
for (auto const &o : pimpl->outputs())
{
m_outputs.emplace_back(o.amount, QString::fromStdString(o.address));
outputs.emplace_back(o.amount, QString::fromStdString(o.address));
}
for (uint32_t i : pimpl->subaddressIndices())
{
m_subaddressIndices.insert(i);
subaddressIndices.insert(i);
}
}
}

View file

@ -4,39 +4,25 @@
#ifndef FEATHER_CONSTRUCTIONINFO_H
#define FEATHER_CONSTRUCTIONINFO_H
#include <QObject>
#include <QSet>
#include "Transfer.h"
class Input;
#include "Transfer.h"
#include "Input.h"
namespace Monero {
class TransactionConstructionInfo;
}
class ConstructionInfo : public QObject
struct ConstructionInfo
{
Q_OBJECT
quint64 unlockTime;
QSet<quint32> subaddressIndices;
QVector<QString> subaddresses;
quint64 minMixinCount;
QList<Input> inputs;
QList<Transfer> outputs;
public:
quint64 unlockTime() const;
QSet<quint32> subaddressIndices() const;
QVector<QString> subaddresses() const;
quint64 minMixinCount() const;
QList<Input*> inputs() const;
QList<Transfer> outputs() const;
private:
explicit ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl, QObject *parent = nullptr);
friend class PendingTransactionInfo;
friend class UnsignedTransaction;
quint64 m_unlockTime;
QSet<quint32> m_subaddressIndices;
QVector<QString> m_subaddresses;
quint64 m_minMixinCount;
mutable QList<Input*> m_inputs;
mutable QList<Transfer> m_outputs;
explicit ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl);
};
#endif //FEATHER_CONSTRUCTIONINFO_H

View file

@ -4,22 +4,14 @@
#ifndef FEATHER_INPUT_H
#define FEATHER_INPUT_H
#include <QObject>
class Input : public QObject
struct Input
{
Q_OBJECT
QString pubKey;
quint64 amount;
private:
explicit Input(uint64_t _amount, QString _address, QObject *parent = nullptr): QObject(parent), m_amount(_amount), m_pubkey(std::move(_address)) {};
friend class ConstructionInfo;
quint64 m_amount;
QString m_pubkey;
public:
quint64 amount() const { return m_amount; }
QString pubKey() const { return m_pubkey; }
explicit Input(uint64_t _amount, QString _pubkey)
: pubKey(std::move(_pubkey))
, amount(_amount) {}
};
#endif //FEATHER_INPUT_H

View file

@ -90,18 +90,17 @@ quint64 PendingTransaction::weight(int index) const
return m_pimpl->weight(index);
}
PendingTransactionInfo * PendingTransaction::transaction(int index) const {
const PendingTransactionInfo& PendingTransaction::transaction(int index) const {
return m_pending_tx_info[index];
}
void PendingTransaction::refresh()
{
qDeleteAll(m_pending_tx_info);
m_pending_tx_info.clear();
m_pimpl->refresh();
for (const auto i : m_pimpl->getAll()) {
m_pending_tx_info.append(new PendingTransactionInfo(i, this));
m_pending_tx_info.emplace_back(i);
}
}

View file

@ -42,7 +42,7 @@ public:
quint64 weight(int index) const;
void refresh();
PendingTransactionInfo * transaction(int index) const;
const PendingTransactionInfo& transaction(int index) const;
private:
explicit PendingTransaction(Monero::PendingTransaction * pt, QObject *parent = nullptr);
@ -50,7 +50,7 @@ private:
private:
friend class Wallet;
Monero::PendingTransaction * m_pimpl;
mutable QList<PendingTransactionInfo*> m_pending_tx_info;
mutable QList<PendingTransactionInfo> m_pending_tx_info;
};
#endif // PENDINGTRANSACTION_H

View file

@ -4,28 +4,12 @@
#include "PendingTransactionInfo.h"
#include <wallet/api/wallet2_api.h>
quint64 PendingTransactionInfo::fee() const {
return m_fee;
}
quint64 PendingTransactionInfo::dust() const {
return m_dust;
}
bool PendingTransactionInfo::dustAddedToFee() const {
return m_dustAddedToFee;
}
QString PendingTransactionInfo::txKey() const {
return m_txKey;
}
PendingTransactionInfo::PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl, QObject *parent)
: ConstructionInfo(pimpl->constructionData(), parent)
, m_fee(pimpl->fee())
, m_dust(pimpl->dust())
, m_dustAddedToFee(pimpl->dustAddedToFee())
, m_txKey(QString::fromStdString(pimpl->txKey()))
PendingTransactionInfo::PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl)
: ConstructionInfo(pimpl->constructionData())
, fee(pimpl->fee())
, dust(pimpl->dust())
, dustAddedToFee(pimpl->dustAddedToFee())
, txKey(QString::fromStdString(pimpl->txKey()))
{
}
}

View file

@ -5,34 +5,21 @@
#define FEATHER_PENDINGTRANSACTIONINFO_H
#include "ConstructionInfo.h"
#include <QObject>
#include <QSet>
class Input;
class Transfer;
#include <QObject>
namespace Monero {
class PendingTransactionInfo;
}
class PendingTransactionInfo : public ConstructionInfo
struct PendingTransactionInfo : ConstructionInfo
{
Q_OBJECT
quint64 fee;
quint64 dust;
bool dustAddedToFee;
QString txKey;
public:
quint64 fee() const;
quint64 dust() const;
bool dustAddedToFee() const;
QString txKey() const;
private:
explicit PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl, QObject *parent = nullptr);
friend class PendingTransaction;
quint64 m_fee;
quint64 m_dust;
bool m_dustAddedToFee;
QString m_txKey;
explicit PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl);
};
#endif //FEATHER_PENDINGTRANSACTIONINFO_H

View file

@ -27,40 +27,19 @@ QString description(tools::wallet2 *wallet2, const tools::wallet2::payment_detai
return description;
}
bool TransactionHistory::transaction(int index, std::function<void (TransactionRow &)> callback)
{
QReadLocker locker(&m_lock);
if (index < 0 || index >= m_rows.size()) {
qCritical("%s: no transaction info for index %d", __FUNCTION__, index);
qCritical("%s: there's %d transactions in backend", __FUNCTION__, this->count());
return false;
}
callback(*m_rows.value(index));
return true;
}
TransactionRow* TransactionHistory::transaction(const QString &id)
{
QReadLocker locker(&m_lock);
auto itr = std::find_if(m_rows.begin(), m_rows.end(),
[&](const TransactionRow * ti) {
return ti->hash() == id;
});
return itr != m_rows.end() ? *itr : nullptr;
}
TransactionRow* TransactionHistory::transaction(int index)
const TransactionRow& TransactionHistory::transaction(int index)
{
if (index < 0 || index >= m_rows.size()) {
return nullptr;
throw std::out_of_range("Index out of range");
}
return m_rows[index];
}
const QList<TransactionRow>& TransactionHistory::getRows()
{
return m_rows;
}
void TransactionHistory::refresh()
{
qDebug() << Q_FUNC_INFO;
@ -106,24 +85,24 @@ void TransactionHistory::refresh()
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
auto* t = new TransactionRow(this);
t->m_paymentId = QString::fromStdString(payment_id);
t->m_coinbase = pd.m_coinbase;
t->m_amount = pd.m_amount;
t->m_balanceDelta = 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_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;
t->m_description = description(m_wallet2, pd);
TransactionRow t;
t.paymentId = QString::fromStdString(payment_id);
t.coinbase = pd.m_coinbase;
t.amount = pd.m_amount;
t.balanceDelta = pd.m_amount;
t.fee = pd.m_fee;
t.direction = TransactionRow::Direction_In;
t.hash = QString::fromStdString(epee::string_tools::pod_to_hex(pd.m_tx_hash));
t.blockHeight = pd.m_block_height;
t.subaddrIndex = { pd.m_subaddr_index.minor };
t.subaddrAccount = pd.m_subaddr_index.major;
t.label = QString::fromStdString(m_wallet2->get_subaddress_label(pd.m_subaddr_index));
t.timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t.confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0;
t.unlockTime = pd.m_unlock_time;
t.description = description(m_wallet2, pd);
m_rows.append(t);
m_rows.append(std::move(t));
}
// confirmed output transactions
@ -153,42 +132,42 @@ void TransactionHistory::refresh()
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
auto* t = new TransactionRow(this);
t->m_paymentId = QString::fromStdString(payment_id);
TransactionRow t;
t.paymentId = QString::fromStdString(payment_id);
t->m_amount = pd.m_amount_out - change;
t->m_balanceDelta = change - pd.m_amount_in;
t->m_fee = fee;
t.amount = pd.m_amount_out - change;
t.balanceDelta = change - pd.m_amount_in;
t.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;
t.direction = TransactionRow::Direction_Out;
t.hash = QString::fromStdString(epee::string_tools::pod_to_hex(hash));
t.blockHeight = pd.m_block_height;
t.description = QString::fromStdString(m_wallet2->get_tx_note(hash));
t.subaddrAccount = pd.m_subaddr_account;
t.label = QString::fromStdString(pd.m_subaddr_indices.size() == 1 ? m_wallet2->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : "");
t.timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t.confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0;
for (uint32_t idx : t->subaddrIndex())
for (uint32_t idx : t.subaddrIndex)
{
t->m_subaddrIndex.insert(idx);
t.subaddrIndex.insert(idx);
}
// single output transaction might contain multiple transfers
for (auto const &d: pd.m_dests)
{
t->m_transfers.emplace_back(
t.transfers.emplace_back(
d.amount,
QString::fromStdString(d.address(m_wallet2->nettype(), pd.m_payment_id, !hasFakePaymentId)));
}
for (auto const &r: pd.m_rings)
{
t->m_rings.emplace_back(
t.rings.emplace_back(
QString::fromStdString(epee::string_tools::pod_to_hex(r.first)),
cryptonote::relative_output_offsets_to_absolute(r.second));
}
m_rows.append(t);
m_rows.append(std::move(t));
}
// unconfirmed output transactions
@ -209,41 +188,41 @@ void TransactionHistory::refresh()
payment_id = payment_id.substr(0,16);
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
auto *t = new TransactionRow(this);
t->m_paymentId = QString::fromStdString(payment_id);
TransactionRow t;
t.paymentId = QString::fromStdString(payment_id);
t->m_amount = pd.m_amount_out - change;
t->m_balanceDelta = change - pd.m_amount_in;
t->m_fee = fee;
t.amount = pd.m_amount_out - change;
t.balanceDelta = change - pd.m_amount_in;
t.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.direction = TransactionRow::Direction_Out;
t.failed = is_failed;
t.pending = true;
t.hash = QString::fromStdString(epee::string_tools::pod_to_hex(hash));
t.description = QString::fromStdString(m_wallet2->get_tx_note(hash));
t.subaddrAccount = pd.m_subaddr_account;
t.label = QString::fromStdString(pd.m_subaddr_indices.size() == 1 ? m_wallet2->get_subaddress_label({pd.m_subaddr_account, *pd.m_subaddr_indices.begin()}) : "");
t.timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t.confirmations = 0;
for (uint32_t idx : t.subaddrIndex)
{
t->m_subaddrIndex.insert(idx);
t.subaddrIndex.insert(idx);
}
for (auto const &d: pd.m_dests)
{
t->m_transfers.emplace_back(
t.transfers.emplace_back(
d.amount,
QString::fromStdString(d.address(m_wallet2->nettype(), pd.m_payment_id, !hasFakePaymentId)));
}
for (auto const &r: pd.m_rings)
{
t->m_rings.emplace_back(
t.rings.emplace_back(
QString::fromStdString(epee::string_tools::pod_to_hex(r.first)),
cryptonote::relative_output_offsets_to_absolute(r.second));
}
m_rows.append(t);
m_rows.append(std::move(t));
}
@ -259,22 +238,24 @@ void TransactionHistory::refresh()
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(this);
t->m_paymentId = QString::fromStdString(payment_id);
t->m_amount = pd.m_amount;
t->m_balanceDelta = 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_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;
t->m_description = description(m_wallet2, pd);
m_rows.append(t);
TransactionRow t;
t.paymentId = QString::fromStdString(payment_id);
t.amount = pd.m_amount;
t.balanceDelta = pd.m_amount;
t.direction = TransactionRow::Direction_In;
t.hash = QString::fromStdString(epee::string_tools::pod_to_hex(pd.m_tx_hash));
t.blockHeight = pd.m_block_height;
t.pending = true;
t.subaddrIndex = { pd.m_subaddr_index.minor };
t.subaddrAccount = pd.m_subaddr_index.major;
t.label = QString::fromStdString(m_wallet2->get_subaddress_label(pd.m_subaddr_index));
t.timestamp = QDateTime::fromSecsSinceEpoch(pd.m_timestamp);
t.confirmations = 0;
t.description = description(m_wallet2, pd);
m_rows.append(std::move(t));
LOG_PRINT_L1(__FUNCTION__ << ": Unconfirmed payment found " << pd.m_amount);
}
@ -341,7 +322,6 @@ TransactionHistory::TransactionHistory(Wallet *wallet, tools::wallet2 *wallet2,
}
void TransactionHistory::clearRows() {
qDeleteAll(m_rows);
m_rows.clear();
}

View file

@ -29,9 +29,9 @@ class TransactionHistory : public QObject
Q_OBJECT
public:
bool transaction(int index, std::function<void (TransactionRow &)> callback);
TransactionRow * transaction(const QString &id);
TransactionRow* transaction(int index);
const TransactionRow& transaction(int index);
const QList<TransactionRow>& getRows();
void refresh();
void setTxNote(const QString &txid, const QString &note);
quint64 count() const;
@ -59,7 +59,7 @@ private:
Wallet *m_wallet;
tools::wallet2 *m_wallet2;
QList<TransactionRow*> m_rows;
QList<TransactionRow> m_rows;
mutable QDateTime m_firstDateTime;
mutable QDateTime m_lastDateTime;

View file

@ -87,18 +87,17 @@ void UnsignedTransaction::setFilename(const QString &fileName)
m_fileName = fileName;
}
ConstructionInfo * UnsignedTransaction::constructionInfo(int index) const {
const ConstructionInfo& UnsignedTransaction::constructionInfo(int index) const {
return m_construction_info[index];
}
void UnsignedTransaction::refresh()
{
qDeleteAll(m_construction_info);
m_construction_info.clear();
m_pimpl->refresh();
for (const auto i : m_pimpl->getAll()) {
m_construction_info.append(new ConstructionInfo(i, this));
m_construction_info.emplace_back(i);
}
}

View file

@ -41,7 +41,7 @@ public:
void setFilename(const QString &fileName);
void refresh();
ConstructionInfo * constructionInfo(int index) const;
const ConstructionInfo& constructionInfo(int index) const;
private:
explicit UnsignedTransaction(Monero::UnsignedTransaction * pt, Monero::Wallet *walletImpl, QObject *parent = nullptr);
@ -51,7 +51,7 @@ private:
Monero::UnsignedTransaction * m_pimpl;
QString m_fileName;
Monero::Wallet * m_walletImpl;
mutable QList<ConstructionInfo*> m_construction_info;
mutable QList<ConstructionInfo> m_construction_info;
};
#endif // UNSIGNEDTRANSACTION_H

View file

@ -5,171 +5,75 @@
#include "WalletManager.h"
#include "Transfer.h"
TransactionRow::TransactionRow(QObject *parent)
: QObject(parent)
, m_direction(TransactionRow::Direction_Out)
, m_pending(false)
, m_failed(false)
, m_coinbase(false)
, m_amount(0)
, m_balanceDelta(0)
, m_fee(0)
, m_blockHeight(0)
, m_subaddrAccount(0)
, m_confirmations(0)
, m_unlockTime(0)
, m_confirmationsRequired(0)
TransactionRow::TransactionRow()
: amount(0)
, balanceDelta(0)
, blockHeight(0)
, confirmations(0)
, direction(TransactionRow::Direction_Out)
, subaddrAccount(0)
, unlockTime(0)
, failed(false)
, pending(false)
, coinbase(false)
, fee(0)
{
}
TransactionRow::Direction TransactionRow::direction() const
double TransactionRow::amountDouble() 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;
}
qint64 TransactionRow::balanceDelta() const
{
return m_balanceDelta;
}
double TransactionRow::amount() const
{
// there's no unsigned uint64 for JS, so better use double
return displayAmount().toDouble();
}
qint64 TransactionRow::atomicAmount() const
{
return m_amount;
}
QString TransactionRow::displayAmount() const
{
return WalletManager::displayAmount(m_amount);
return WalletManager::displayAmount(amount);
}
quint64 TransactionRow::atomicFee() const
QString TransactionRow::displayFee() const
{
return m_fee;
}
QString TransactionRow::fee() const
{
if(m_fee == 0)
if (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;
return WalletManager::displayAmount(fee);
}
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;
return (blockHeight < unlockTime) ? unlockTime - blockHeight : 10;
}
QString TransactionRow::date() const
{
return timestamp().date().toString(Qt::ISODate);
return timestamp.date().toString(Qt::ISODate);
}
QString TransactionRow::time() const
{
return timestamp().time().toString(Qt::ISODate);
}
QString TransactionRow::paymentId() const
{
return m_paymentId;
return timestamp.time().toString(Qt::ISODate);
}
QList<QString> TransactionRow::destinations() const
{
QList<QString> dests;
for (auto const& t: m_transfers) {
for (auto const& t: 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";
QString ringsStr;
for (auto const& r: rings) {
ringsStr += r.keyImage + ": \n";
for (uint64_t m : r.ringMembers){
rings += QString::number(m) + " ";
ringsStr += QString::number(m) + " ";
}
rings += "\n\n";
ringsStr += "\n\n";
}
return rings;
return ringsStr;
}
bool TransactionRow::hasPaymentId() const {
return m_paymentId != "0000000000000000";
}
TransactionRow::~TransactionRow()
{
return paymentId != "0000000000000000";
}

View file

@ -4,7 +4,6 @@
#ifndef FEATHER_TRANSACTIONROW_H
#define FEATHER_TRANSACTIONROW_H
#include <QObject>
#include <QSet>
#include <QDateTime>
@ -19,75 +18,45 @@ struct Ring
};
struct Transfer;
class TransactionRow : public QObject
struct TransactionRow
{
Q_OBJECT
public:
~TransactionRow() override;
enum Direction {
Direction_In = 0,
Direction_Out = 1,
Direction_Both // invalid direction value, used for filtering
};
Q_ENUM(Direction)
QList<Transfer> transfers;
QList<Ring> rings;
qint64 amount; // Amount that was sent (to destinations) or received, excludes tx fee
qint64 balanceDelta; // How much the total balance was mutated as a result of this tx (includes tx fee)
quint64 blockHeight;
QString description;
quint64 confirmations;
Direction direction;
QString hash;
QString label;
QString paymentId;
quint32 subaddrAccount;
QSet<quint32> subaddrIndex;
QDateTime timestamp;
quint64 unlockTime;
bool failed;
bool pending;
bool coinbase;
quint64 fee;
Direction direction() const;
bool isPending() const;
bool isFailed() const;
bool isCoinbase() const;
qint64 balanceDelta() const;
double amount() const;
qint64 atomicAmount() const;
QString displayFee() const;
QString displayAmount() const;
QString fee() const;
quint64 atomicFee() const;
quint64 blockHeight() const;
QString description() const;
QSet<quint32> subaddrIndex() const;
quint32 subaddrAccount() const;
QString label() const;
quint64 confirmations() const;
double amountDouble() const;
quint64 confirmationsRequired() const;
quint64 unlockTime() const;
QString hash() const;
QDateTime timestamp() const;
QString date() const;
QString time() const;
QString paymentId() const;
QList<QString> destinations() const;
QList<Transfer> transfers() const;
QString rings_formatted() const;
bool hasPaymentId() const;
private:
explicit TransactionRow(QObject *parent);
private:
friend class TransactionHistory;
QList<Transfer> m_transfers;
QList<Ring> m_rings;
qint64 m_amount; // Amount that was sent (to destinations) or received, excludes tx fee
qint64 m_balanceDelta; // How much the total balance was mutated as a result of this tx (includes tx fee)
quint64 m_blockHeight;
QString m_description;
quint64 m_confirmations;
quint64 m_confirmationsRequired;
Direction m_direction;
bool m_failed;
quint64 m_fee;
QString m_hash;
QString m_label;
QString m_paymentId;
bool m_pending;
quint32 m_subaddrAccount;
QSet<quint32> m_subaddrIndex;
QDateTime m_timestamp;
quint64 m_unlockTime;
bool m_coinbase;
explicit TransactionRow();
};
#endif //FEATHER_TRANSACTIONROW_H

View file

@ -75,16 +75,6 @@ TransactionHistoryModel* HistoryView::sourceModel()
return dynamic_cast<TransactionHistoryModel *>(m_model->sourceModel());
}
TransactionRow* HistoryView::currentEntry()
{
QModelIndexList list = selectionModel()->selectedRows();
if (list.size() == 1) {
return this->sourceModel()->entryFromIndex(m_model->mapToSource(list.first()));
} else {
return nullptr;
}
}
void HistoryView::setSearchMode(bool mode) {
if (!m_inSearchMode) {
m_showTxidColumn = !header()->isSectionHidden(TransactionHistoryModel::TxID);
@ -213,12 +203,26 @@ void HistoryView::resetViewToDefaults()
}
void HistoryView::keyPressEvent(QKeyEvent *event) {
TransactionRow* tx = this->currentEntry();
auto index = this->getCurrentIndex();
if (!index.isValid()) {
return;
}
const TransactionRow& tx = sourceModel()->entryFromIndex(index);
if (event->matches(QKeySequence::Copy) && tx) {
Utils::copyToClipboard(tx->hash());
if (event->matches(QKeySequence::Copy)) {
Utils::copyToClipboard(tx.hash);
}
else {
QTreeView::keyPressEvent(event);
}
}
}
QModelIndex HistoryView::getCurrentIndex()
{
QModelIndexList list = this->selectionModel()->selectedRows();
if (list.length() < 1) {
return {};
}
return m_model->mapToSource(list.first());
}

View file

@ -18,11 +18,12 @@ class HistoryView : public QTreeView
public:
explicit HistoryView(QWidget* parent = nullptr);
void setHistoryModel(TransactionHistoryProxyModel *model);
TransactionRow* currentEntry();
void setSearchMode(bool mode);
QByteArray viewState() const;
bool setViewState(const QByteArray& state);
QModelIndex getCurrentIndex();
TransactionHistoryModel* sourceModel();
private slots:
void showHeaderMenu(const QPoint& position);
@ -36,8 +37,6 @@ protected:
void keyPressEvent(QKeyEvent *event);
private:
TransactionHistoryModel* sourceModel();
TransactionHistoryProxyModel* m_model;
bool m_inSearchMode = false;
bool m_columnsNeedRelayout = true;

View file

@ -33,7 +33,7 @@ TransactionHistory *TransactionHistoryModel::transactionHistory() const {
return m_transactionHistory;
}
TransactionRow* TransactionHistoryModel::entryFromIndex(const QModelIndex &index) const {
const TransactionRow& TransactionHistoryModel::entryFromIndex(const QModelIndex &index) const {
Q_ASSERT(index.isValid() && index.row() < m_transactionHistory->count());
return m_transactionHistory->transaction(index.row());
}
@ -55,87 +55,79 @@ int TransactionHistoryModel::columnCount(const QModelIndex &parent) const {
}
QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const {
if (!m_transactionHistory) {
return QVariant();
const QList<TransactionRow>& rows = m_transactionHistory->getRows();
if (index.row() < 0 || index.row() >= rows.size()) {
return {};
}
const TransactionRow& tInfo = rows[index.row()];
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
return parseTransactionInfo(tInfo, index.column(), role);
}
else if (role == Qt::TextAlignmentRole) {
switch (index.column()) {
case Column::Amount:
case Column::FiatAmount:
return Qt::AlignRight;
}
}
else if (role == Qt::DecorationRole) {
switch (index.column()) {
case Column::Date:
{
if (tInfo.failed)
return QVariant(icons()->icon("warning.png"));
else if (tInfo.pending)
return QVariant(icons()->icon("unconfirmed.png"));
else if (tInfo.confirmations <= (1.0/5.0 * tInfo.confirmationsRequired()))
return QVariant(icons()->icon("clock1.png"));
else if (tInfo.confirmations <= (2.0/5.0 * tInfo.confirmationsRequired()))
return QVariant(icons()->icon("clock2.png"));
else if (tInfo.confirmations <= (3.0/5.0 * tInfo.confirmationsRequired()))
return QVariant(icons()->icon("clock3.png"));
else if (tInfo.confirmations <= (4.0/5.0 * tInfo.confirmationsRequired()))
return QVariant(icons()->icon("clock4.png"));
else if (tInfo.confirmations < tInfo.confirmationsRequired())
return QVariant(icons()->icon("clock5.png"));
else if (tInfo.confirmations)
return QVariant(icons()->icon("confirmed.svg"));
}
}
}
else if (role == Qt::ToolTipRole) {
switch(index.column()) {
case Column::Date:
{
if (tInfo.failed)
return "Transaction failed";
else if (tInfo.confirmations < tInfo.confirmationsRequired())
return QString("%1/%2 confirmations").arg(QString::number(tInfo.confirmations), QString::number(tInfo.confirmationsRequired()));
else
return QString("%1 confirmations").arg(QString::number(tInfo.confirmations));
}
}
}
else if (role == Qt::ForegroundRole) {
switch(index.column()) {
case Column::FiatAmount:
case Column::Amount:
{
if (tInfo.balanceDelta < 0) {
return QVariant(QColor("#BC1E1E"));
}
}
}
}
else if (role == Qt::FontRole) {
switch(index.column()) {
case Column::TxID:
{
return Utils::getMonospaceFont();
}
}
}
if (!index.isValid() || index.row() < 0 || static_cast<quint64>(index.row()) >= m_transactionHistory->count())
return QVariant();
QVariant result;
bool found = m_transactionHistory->transaction(index.row(), [this, &index, &result, &role](const TransactionRow &tInfo) {
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
result = parseTransactionInfo(tInfo, index.column(), role);
}
else if (role == Qt::TextAlignmentRole) {
switch (index.column()) {
case Column::Amount:
case Column::FiatAmount:
result = Qt::AlignRight;
}
}
else if (role == Qt::DecorationRole) {
switch (index.column()) {
case Column::Date:
{
if (tInfo.isFailed())
result = QVariant(icons()->icon("warning.png"));
else if (tInfo.isPending())
result = QVariant(icons()->icon("unconfirmed.png"));
else if (tInfo.confirmations() <= (1.0/5.0 * tInfo.confirmationsRequired()))
result = QVariant(icons()->icon("clock1.png"));
else if (tInfo.confirmations() <= (2.0/5.0 * tInfo.confirmationsRequired()))
result = QVariant(icons()->icon("clock2.png"));
else if (tInfo.confirmations() <= (3.0/5.0 * tInfo.confirmationsRequired()))
result = QVariant(icons()->icon("clock3.png"));
else if (tInfo.confirmations() <= (4.0/5.0 * tInfo.confirmationsRequired()))
result = QVariant(icons()->icon("clock4.png"));
else if (tInfo.confirmations() < tInfo.confirmationsRequired())
result = QVariant(icons()->icon("clock5.png"));
else if (tInfo.confirmations())
result = QVariant(icons()->icon("confirmed.svg"));
}
}
}
else if (role == Qt::ToolTipRole) {
switch(index.column()) {
case Column::Date:
{
if (tInfo.isFailed())
result = "Transaction failed";
else if (tInfo.confirmations() < tInfo.confirmationsRequired())
result = QString("%1/%2 confirmations").arg(QString::number(tInfo.confirmations()), QString::number(tInfo.confirmationsRequired()));
else
result = QString("%1 confirmations").arg(QString::number(tInfo.confirmations()));
}
}
}
else if (role == Qt::ForegroundRole) {
switch(index.column()) {
case Column::FiatAmount:
case Column::Amount:
{
if (tInfo.balanceDelta() < 0) {
result = QVariant(QColor("#BC1E1E"));
}
}
}
}
else if (role == Qt::FontRole) {
switch(index.column()) {
case Column::TxID:
{
result = Utils::getMonospaceFont();
}
}
}
});
if (!found) {
qCritical("%s: internal error: no transaction info for index %d", __FUNCTION__, index.row());
}
return result;
return {};
}
QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionRow &tInfo, int column, int role) const
@ -145,39 +137,39 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionRow &tIn
case Column::Date:
{
if (role == Qt::UserRole) {
if (tInfo.blockHeight() > 0) {
return tInfo.blockHeight();
if (tInfo.blockHeight > 0) {
return tInfo.blockHeight;
}
return tInfo.timestamp().toMSecsSinceEpoch();
return tInfo.timestamp.toMSecsSinceEpoch();
}
return tInfo.timestamp().toString(QString("%1 %2 ").arg(conf()->get(Config::dateFormat).toString(),
return tInfo.timestamp.toString(QString("%1 %2 ").arg(conf()->get(Config::dateFormat).toString(),
conf()->get(Config::timeFormat).toString()));
}
case Column::Description:
return tInfo.description();
return tInfo.description;
case Column::Amount:
{
if (role == Qt::UserRole) {
return tInfo.balanceDelta();
return tInfo.balanceDelta;
}
QString amount = QString::number(tInfo.balanceDelta() / constants::cdiv, 'f', conf()->get(Config::amountPrecision).toInt());
amount = (tInfo.balanceDelta() < 0) ? amount : "+" + amount;
QString amount = QString::number(tInfo.balanceDelta / constants::cdiv, 'f', conf()->get(Config::amountPrecision).toInt());
amount = (tInfo.balanceDelta < 0) ? amount : "+" + amount;
return amount;
}
case Column::TxID: {
if (conf()->get(Config::historyShowFullTxid).toBool()) {
return tInfo.hash();
return tInfo.hash;
}
return Utils::displayAddress(tInfo.hash(), 1);
return Utils::displayAddress(tInfo.hash, 1);
}
case Column::FiatAmount:
{
double usd_price = appData()->txFiatHistory->get(tInfo.timestamp().toString("yyyyMMdd"));
double usd_price = appData()->txFiatHistory->get(tInfo.timestamp.toString("yyyyMMdd"));
if (usd_price == 0.0) {
return QString("?");
}
double usd_amount = usd_price * (abs(tInfo.balanceDelta()) / constants::cdiv);
double usd_amount = usd_price * (abs(tInfo.balanceDelta) / constants::cdiv);
QString preferredFiatCurrency = conf()->get(Config::preferredFiatCurrency).toString();
if (preferredFiatCurrency != "USD") {
@ -227,15 +219,11 @@ QVariant TransactionHistoryModel::headerData(int section, Qt::Orientation orient
bool TransactionHistoryModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (index.isValid() && role == Qt::EditRole) {
QString hash;
switch (index.column()) {
case Column::Description:
{
m_transactionHistory->transaction(index.row(), [this, &hash, &value](const TransactionRow &tInfo){
hash = tInfo.hash();
});
m_transactionHistory->setTxNote(hash, value.toString());
const TransactionRow& row = m_transactionHistory->transaction(index.row());
m_transactionHistory->setTxNote(row.hash, value.toString());
m_transactionHistory->refresh();
emit transactionDescriptionChanged();
break;

View file

@ -33,7 +33,7 @@ public:
explicit TransactionHistoryModel(QObject * parent = nullptr);
void setTransactionHistory(TransactionHistory * th);
TransactionHistory * transactionHistory() const;
TransactionRow* entryFromIndex(const QModelIndex& index) const;
const TransactionRow& entryFromIndex(const QModelIndex& index) const;
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;

View file

@ -21,17 +21,17 @@ TransactionHistory* TransactionHistoryProxyModel::history() {
bool TransactionHistoryProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QString description, txid, subaddrlabel;
quint32 subaddrAccount;
QSet<quint32> subaddrIndex;
if (sourceRow < 0 || sourceRow >= m_history->count()) {
return false;
}
m_history->transaction(sourceRow, [&description, &txid, &subaddrlabel, &subaddrAccount, &subaddrIndex](TransactionRow &tInfo){
description = tInfo.description();
txid = tInfo.hash();
subaddrlabel = tInfo.label();
subaddrAccount = tInfo.subaddrAccount();
subaddrIndex = tInfo.subaddrIndex();
});
const TransactionRow& row = m_history->transaction(sourceRow);
QString description = row.description;
QString txid = row.hash;
QString subaddrlabel = row.label;
quint32 subaddrAccount = row.subaddrAccount;
QSet<quint32> subaddrIndex = row.subaddrIndex;
bool addressFound;
for (quint32 i : subaddrIndex) {
@ -41,4 +41,4 @@ bool TransactionHistoryProxyModel::filterAcceptsRow(int sourceRow, const QModelI
}
return (description.contains(m_searchRegExp) || txid.contains(m_searchRegExp) || subaddrlabel.contains(m_searchRegExp)) || addressFound;
}
}