mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 19:49:28 +00:00
History: rework
This commit is contained in:
parent
59586f4b6e
commit
cc5b3c3c27
34 changed files with 1504 additions and 632 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@ feather.cbp
|
||||||
src/tor/*
|
src/tor/*
|
||||||
!src/tor/.gitkeep
|
!src/tor/.gitkeep
|
||||||
src/config-feather.h
|
src/config-feather.h
|
||||||
|
src/assets/exec/*
|
||||||
|
|
|
@ -19,7 +19,6 @@ Prices *AppContext::prices = nullptr;
|
||||||
WalletKeysFilesModel *AppContext::wallets = nullptr;
|
WalletKeysFilesModel *AppContext::wallets = nullptr;
|
||||||
TxFiatHistory *AppContext::txFiatHistory = nullptr;
|
TxFiatHistory *AppContext::txFiatHistory = nullptr;
|
||||||
double AppContext::balance = 0;
|
double AppContext::balance = 0;
|
||||||
QMap<QString, QString> AppContext::txDescriptionCache;
|
|
||||||
QMap<QString, QString> AppContext::txCache;
|
QMap<QString, QString> AppContext::txCache;
|
||||||
|
|
||||||
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
|
@ -303,6 +302,13 @@ void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppContext::onAmountPrecisionChanged(int precision) {
|
||||||
|
if (!this->currentWallet) return;
|
||||||
|
auto *model = this->currentWallet->transactionHistoryModel();
|
||||||
|
if (!model) return;
|
||||||
|
model->amountPrecision = precision;
|
||||||
|
}
|
||||||
|
|
||||||
void AppContext::onWalletOpened(Wallet *wallet) {
|
void AppContext::onWalletOpened(Wallet *wallet) {
|
||||||
auto state = wallet->status();
|
auto state = wallet->status();
|
||||||
if (state != Wallet::Status_Ok) {
|
if (state != Wallet::Status_Ok) {
|
||||||
|
@ -770,21 +776,28 @@ void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QStr
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
|
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
|
||||||
this->currentWallet->history()->refresh(this->currentWallet->currentSubaddressAccount());
|
if (status) {
|
||||||
this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount());
|
for (const auto &entry: txid) {
|
||||||
|
this->currentWallet->setUserNote(entry, this->tmpTxDescription);
|
||||||
|
}
|
||||||
|
this->tmpTxDescription = "";
|
||||||
|
}
|
||||||
|
|
||||||
// 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->currentWallet->store();
|
this->currentWallet->store();
|
||||||
|
|
||||||
this->updateBalance();
|
this->currentWallet->history()->refresh(this->currentWallet->currentSubaddressAccount());
|
||||||
|
this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount());
|
||||||
|
|
||||||
emit transactionCommitted(status, tx, txid);
|
this->updateBalance();
|
||||||
|
|
||||||
// this tx was a donation to Feather, stop our nagging
|
// this tx was a donation to Feather, stop our nagging
|
||||||
if(this->donationSending) {
|
if(this->donationSending) {
|
||||||
this->donationSending = false;
|
this->donationSending = false;
|
||||||
config()->set(Config::donateBeg, -1);
|
config()->set(Config::donateBeg, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit transactionCommitted(status, tx, txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppContext::storeWallet() {
|
void AppContext::storeWallet() {
|
||||||
|
|
|
@ -85,7 +85,6 @@ public:
|
||||||
static Prices *prices;
|
static Prices *prices;
|
||||||
static WalletKeysFilesModel *wallets;
|
static WalletKeysFilesModel *wallets;
|
||||||
static double balance;
|
static double balance;
|
||||||
static QMap<QString, QString> txDescriptionCache;
|
|
||||||
static QMap<QString, QString> txCache;
|
static QMap<QString, QString> txCache;
|
||||||
static TxFiatHistory *txFiatHistory;
|
static TxFiatHistory *txFiatHistory;
|
||||||
|
|
||||||
|
@ -119,6 +118,7 @@ public slots:
|
||||||
void onOpenAliasResolve(const QString &openAlias);
|
void onOpenAliasResolve(const QString &openAlias);
|
||||||
void onSetRestoreHeight(quint64 height);
|
void onSetRestoreHeight(quint64 height);
|
||||||
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
void onPreferredFiatCurrencyChanged(const QString &symbol);
|
||||||
|
void onAmountPrecisionChanged(int precision);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onWSNodes(const QJsonArray &nodes);
|
void onWSNodes(const QJsonArray &nodes);
|
||||||
|
|
226
src/dialog/TxProofDialog.cpp
Normal file
226
src/dialog/TxProofDialog.cpp
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020-2021, The Monero Project.
|
||||||
|
|
||||||
|
#include "TxProofDialog.h"
|
||||||
|
#include "ui_TxProofDialog.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "libwalletqt/Transfer.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
TxProofDialog::TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txInfo)
|
||||||
|
: QDialog(parent)
|
||||||
|
, ui(new Ui::TxProofDialog)
|
||||||
|
, m_wallet(wallet)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
m_txid = txInfo->hash();
|
||||||
|
m_txKey = m_wallet->getTxKey(m_txid);
|
||||||
|
m_direction = txInfo->direction();
|
||||||
|
|
||||||
|
for (auto const &t: txInfo->transfers()) {
|
||||||
|
m_OutDestinations.push_back(t->address());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const &s: txInfo->subaddrIndex()) {
|
||||||
|
m_InDestinations.push_back(wallet->address(txInfo->subaddrAccount(), s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to some logic in core we can't create OutProofs
|
||||||
|
// for churn transactions that sweep from and send to the same address
|
||||||
|
for (auto const &address : m_InDestinations) {
|
||||||
|
m_OutDestinations.removeAll(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ui->radio_SpendProof, &QRadioButton::toggled, this, &TxProofDialog::selectSpendProof);
|
||||||
|
connect(ui->radio_OutProof, &QRadioButton::toggled, this, &TxProofDialog::selectOutProof);
|
||||||
|
connect(ui->radio_InProof, &QRadioButton::toggled, this, &TxProofDialog::selectInProof);
|
||||||
|
|
||||||
|
connect(ui->btn_getFormattedProof, &QPushButton::pressed, this, &TxProofDialog::getFormattedProof);
|
||||||
|
connect(ui->btn_getSignature, &QPushButton::pressed, this, &TxProofDialog::getSignature);
|
||||||
|
|
||||||
|
ui->radio_SpendProof->setChecked(true);
|
||||||
|
ui->label_txid->setText(m_txid);
|
||||||
|
|
||||||
|
ui->group_summary->hide(); // todo
|
||||||
|
|
||||||
|
this->adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::setTxId(const QString &txid) {
|
||||||
|
ui->label_txid->setText(txid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::selectSpendProof() {
|
||||||
|
m_mode = Mode::SpendProof;
|
||||||
|
this->resetFrames();
|
||||||
|
|
||||||
|
if (m_direction == TransactionInfo::Direction_In) {
|
||||||
|
this->showWarning("Your wallet did not construct this transaction. Creating a SpendProof is not possible.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->frame_message->show();
|
||||||
|
ui->label_summary->setText("This proof shows you created a transaction with the txid shown above.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::selectOutProof() {
|
||||||
|
m_mode = Mode::OutProof;
|
||||||
|
this->resetFrames();
|
||||||
|
|
||||||
|
if (m_OutDestinations.empty()) {
|
||||||
|
this->showWarning("This transaction did not spend any outputs owned by this wallet. Creating an OutProof is not possible.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_txKey.isEmpty()) {
|
||||||
|
this->showWarning("No transaction key stored for this transaction. Creating an OutProof is not possible.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->selectTxProof();
|
||||||
|
ui->combo_address->addItems(m_OutDestinations);
|
||||||
|
ui->label_summary->setText("This proof shows you paid x XMR to the address selected above.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::selectInProof() {
|
||||||
|
m_mode = Mode::InProof;
|
||||||
|
this->resetFrames();
|
||||||
|
|
||||||
|
if (m_InDestinations.empty()) {
|
||||||
|
this->showWarning("Your wallet did not receive any outputs in this transaction.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->selectTxProof();
|
||||||
|
ui->combo_address->addItems(m_InDestinations);
|
||||||
|
ui->label_summary->setText("This proof shows you received x XMR to the address selected above.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::selectTxProof() {
|
||||||
|
ui->frame_txKeyWarning->hide();
|
||||||
|
ui->frame_message->show();
|
||||||
|
ui->frame_address->show();
|
||||||
|
ui->combo_address->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::resetFrames() {
|
||||||
|
ui->frame_txKeyWarning->hide();
|
||||||
|
ui->frame_message->hide();
|
||||||
|
ui->frame_address->hide();
|
||||||
|
this->toggleButtons(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::toggleButtons(bool enabled) {
|
||||||
|
ui->btn_getFormattedProof->setEnabled(enabled);
|
||||||
|
ui->btn_getSignature->setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::showWarning(const QString &message) {
|
||||||
|
this->toggleButtons(false);
|
||||||
|
ui->frame_txKeyWarning->show();
|
||||||
|
ui->label_txKeyWarning->setText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::getFormattedProof() {
|
||||||
|
QString message = ui->message->toPlainText();
|
||||||
|
QString address = ui->combo_address->currentText();
|
||||||
|
QString nettype = Utils::QtEnumToString(m_wallet->nettype()).toLower();
|
||||||
|
nettype = nettype.replace(0, 1, nettype[0].toUpper()); // Capitalize first letter
|
||||||
|
|
||||||
|
TxProof proof = this->getProof();
|
||||||
|
|
||||||
|
if (!proof.error.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Get formatted proof", QString("Failed to get proof signature: %1").arg(proof.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList signatureSplit;
|
||||||
|
for (int i = 0; i < proof.proof.length(); i += 64) {
|
||||||
|
signatureSplit.append(proof.proof.mid(i, 64));
|
||||||
|
}
|
||||||
|
QString signature = signatureSplit.join('\n');
|
||||||
|
|
||||||
|
QString formattedProof = [this, nettype, message, address, signature]{
|
||||||
|
switch (m_mode) {
|
||||||
|
case Mode::SpendProof: {
|
||||||
|
return QString("-----BEGIN SPENDPROOF-----\n"
|
||||||
|
"Network: Monero %1\n"
|
||||||
|
"Txid: %2\n"
|
||||||
|
"\n"
|
||||||
|
"%3\n"
|
||||||
|
"-----BEGIN SPENDPROOF SIGNATURE-----\n"
|
||||||
|
"\n"
|
||||||
|
"%4\n"
|
||||||
|
"-----END SPENDPROOF SIGNATURE-----").arg(nettype, m_txid, message, signature);
|
||||||
|
}
|
||||||
|
case Mode::OutProof: {
|
||||||
|
return QString("-----BEGIN OUTPROOF-----\n"
|
||||||
|
"Network: Monero %1\n"
|
||||||
|
"Txid: %2\n"
|
||||||
|
"Address: %3\n"
|
||||||
|
"\n"
|
||||||
|
"%4\n"
|
||||||
|
"-----BEGIN OUTPROOF SIGNATURE-----\n"
|
||||||
|
"\n"
|
||||||
|
"%5\n"
|
||||||
|
"-----END OUTPROOF SIGNATURE-----").arg(nettype, m_txid, address, message, signature);
|
||||||
|
}
|
||||||
|
case Mode::InProof: {
|
||||||
|
return QString("-----BEGIN INPROOF-----\n"
|
||||||
|
"Network: Monero %1\n"
|
||||||
|
"Txid: %2\n"
|
||||||
|
"Address: %3\n"
|
||||||
|
"\n"
|
||||||
|
"%4\n"
|
||||||
|
"-----BEGIN INPROOF SIGNATURE-----\n"
|
||||||
|
"\n"
|
||||||
|
"%5\n"
|
||||||
|
"-----END INPROOF SIGNATURE-----").arg(nettype, m_txid, address, message, signature);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return QString("");
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
Utils::copyToClipboard(formattedProof);
|
||||||
|
QMessageBox::information(this, "Get formatted proof", "Formatted proof copied to clipboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TxProofDialog::getSignature() {
|
||||||
|
TxProof proof = this->getProof();
|
||||||
|
|
||||||
|
if (!proof.error.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Get proof signature", QString("Failed to get proof signature: %1").arg(proof.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::copyToClipboard(proof.proof);
|
||||||
|
QMessageBox::information(this, "Get proof singature", "Proof signature copied to clipboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
TxProof TxProofDialog::getProof() {
|
||||||
|
QString message = ui->message->toPlainText();
|
||||||
|
QString address = ui->combo_address->currentText();
|
||||||
|
|
||||||
|
TxProof proof = [this, message, address]{
|
||||||
|
switch (m_mode) {
|
||||||
|
case Mode::SpendProof: {
|
||||||
|
return m_wallet->getSpendProof(m_txid, message);
|
||||||
|
}
|
||||||
|
case Mode::OutProof:
|
||||||
|
case Mode::InProof: { // Todo: split this into separate functions
|
||||||
|
return m_wallet->getTxProof(m_txid, address, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
return proof;
|
||||||
|
}
|
||||||
|
|
||||||
|
TxProofDialog::~TxProofDialog() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
56
src/dialog/TxProofDialog.h
Normal file
56
src/dialog/TxProofDialog.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020-2021, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef FEATHER_TXPROOFDIALOG_H
|
||||||
|
#define FEATHER_TXPROOFDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "libwalletqt/Wallet.h"
|
||||||
|
#include "libwalletqt/TransactionInfo.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class TxProofDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TxProofDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TxProofDialog(QWidget *parent, Wallet *wallet, TransactionInfo *txid);
|
||||||
|
~TxProofDialog() override;
|
||||||
|
void setTxId(const QString &txid);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void selectSpendProof();
|
||||||
|
void selectOutProof();
|
||||||
|
void selectInProof();
|
||||||
|
void selectTxProof();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Mode {
|
||||||
|
SpendProof = 0,
|
||||||
|
OutProof,
|
||||||
|
InProof
|
||||||
|
};
|
||||||
|
|
||||||
|
void getFormattedProof();
|
||||||
|
void getSignature();
|
||||||
|
TxProof getProof();
|
||||||
|
void resetFrames();
|
||||||
|
void toggleButtons(bool enabled);
|
||||||
|
void showWarning(const QString &message);
|
||||||
|
|
||||||
|
QStringList m_OutDestinations;
|
||||||
|
QStringList m_InDestinations;
|
||||||
|
QString m_txid;
|
||||||
|
QString m_txKey;
|
||||||
|
Mode m_mode;
|
||||||
|
TransactionInfo::Direction m_direction;
|
||||||
|
|
||||||
|
Ui::TxProofDialog *ui;
|
||||||
|
Wallet *m_wallet;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_TXPROOFDIALOG_H
|
301
src/dialog/TxProofDialog.ui
Normal file
301
src/dialog/TxProofDialog.ui
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>TxProofDialog</class>
|
||||||
|
<widget class="QDialog" name="TxProofDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>542</width>
|
||||||
|
<height>827</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Create Tx Proof</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="group_proofType">
|
||||||
|
<property name="title">
|
||||||
|
<string>Select proof type:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_SpendProof">
|
||||||
|
<property name="text">
|
||||||
|
<string>Prove authorship of a transaction (SpendProof)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_OutProof">
|
||||||
|
<property name="text">
|
||||||
|
<string>Prove a payment to an address (OutProof)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_InProof">
|
||||||
|
<property name="text">
|
||||||
|
<string>Prove ownership of an output (InProof)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>10</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Txid:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_txid">
|
||||||
|
<property name="text">
|
||||||
|
<string>txid</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>10</width>
|
||||||
|
<height>5</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_txKeyWarning">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_txKeyWarning">
|
||||||
|
<property name="text">
|
||||||
|
<string>No transaction key stored for this transaction. Creating an OutProof is not possible.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_message">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Message: (optional)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="message"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_address">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>5</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Address:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="combo_address"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>10</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="group_summary">
|
||||||
|
<property name="title">
|
||||||
|
<string>Summary:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_summary">
|
||||||
|
<property name="text">
|
||||||
|
<string>This proof shows I paid x XMR to ADDR.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_getFormattedProof">
|
||||||
|
<property name="text">
|
||||||
|
<string>Get Formatted Proof</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_getSignature">
|
||||||
|
<property name="text">
|
||||||
|
<string>Get Signature</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -20,21 +20,18 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
m_txProofWidget = new TxProofWidget(this, wallet, txInfo);
|
m_txid = txInfo->hash();
|
||||||
|
ui->label_txid->setText(m_txid);
|
||||||
|
|
||||||
ui->label_txid->setText(QString(txInfo->hash()));
|
|
||||||
|
|
||||||
if (txInfo->direction() == TransactionInfo::Direction_In) {
|
|
||||||
ui->frameTxKey->hide();
|
|
||||||
} else {
|
|
||||||
QString txKey = m_wallet->getTxKey(txInfo->hash());
|
QString txKey = m_wallet->getTxKey(txInfo->hash());
|
||||||
if (txKey.isEmpty()) {
|
if (txKey.isEmpty()) {
|
||||||
ui->btn_CopyTxKey->setEnabled(false);
|
ui->btn_CopyTxKey->setEnabled(false);
|
||||||
ui->btn_CopyTxKey->setToolTip("Transaction key unknown");
|
ui->btn_CopyTxKey->setToolTip("Transaction key unknown");
|
||||||
}
|
}
|
||||||
m_txKey = txKey;
|
m_txKey = txKey;
|
||||||
}
|
|
||||||
connect(ui->btn_CopyTxKey, &QPushButton::pressed, this, &TransactionInfoDialog::copyTxKey);
|
connect(ui->btn_CopyTxKey, &QPushButton::pressed, this, &TransactionInfoDialog::copyTxKey);
|
||||||
|
connect(ui->btn_createTxProof, &QPushButton::pressed, this, &TransactionInfoDialog::createTxProof);
|
||||||
|
|
||||||
QString blockHeight = QString::number(txInfo->blockHeight());
|
QString blockHeight = QString::number(txInfo->blockHeight());
|
||||||
if (blockHeight == "0")
|
if (blockHeight == "0")
|
||||||
|
@ -66,7 +63,7 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
|
||||||
ui->frameDestinations->hide();
|
ui->frameDestinations->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->txProofWidget->addWidget(m_txProofWidget);
|
m_txProofDialog = new TxProofDialog(this, m_wallet, txInfo);
|
||||||
|
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
@ -75,6 +72,10 @@ void TransactionInfoDialog::copyTxKey() {
|
||||||
Utils::copyToClipboard(m_txKey);
|
Utils::copyToClipboard(m_txKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransactionInfoDialog::createTxProof() {
|
||||||
|
m_txProofDialog->show();
|
||||||
|
}
|
||||||
|
|
||||||
TransactionInfoDialog::~TransactionInfoDialog() {
|
TransactionInfoDialog::~TransactionInfoDialog() {
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "libwalletqt/Coins.h"
|
#include "libwalletqt/Coins.h"
|
||||||
#include "libwalletqt/TransactionInfo.h"
|
#include "libwalletqt/TransactionInfo.h"
|
||||||
#include "libwalletqt/Wallet.h"
|
#include "libwalletqt/Wallet.h"
|
||||||
#include "widgets/txproofwidget.h"
|
#include "dialog/TxProofDialog.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class TransactionInfoDialog;
|
class TransactionInfoDialog;
|
||||||
|
@ -26,13 +26,15 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void copyTxKey();
|
void copyTxKey();
|
||||||
|
void createTxProof();
|
||||||
|
|
||||||
Ui::TransactionInfoDialog *ui;
|
Ui::TransactionInfoDialog *ui;
|
||||||
|
|
||||||
|
TxProofDialog *m_txProofDialog;
|
||||||
TransactionInfo *m_txInfo;
|
TransactionInfo *m_txInfo;
|
||||||
Wallet *m_wallet;
|
Wallet *m_wallet;
|
||||||
TxProofWidget *m_txProofWidget;
|
|
||||||
QString m_txKey;
|
QString m_txKey;
|
||||||
|
QString m_txid;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_TRANSACTIONINFODIALOG_H
|
#endif //FEATHER_TRANSACTIONINFODIALOG_H
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>829</width>
|
<width>829</width>
|
||||||
<height>570</height>
|
<height>493</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -187,16 +187,47 @@
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeType">
|
<property name="sizeType">
|
||||||
<enum>QSizePolicy::Maximum</enum>
|
<enum>QSizePolicy::Minimum</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
<height>15</height>
|
<height>10</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_CopyTxKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>Copy Transaction Key</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_createTxProof">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create Transaction Proof</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frameTxKey">
|
<widget class="QFrame" name="frameTxKey">
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
|
@ -218,39 +249,9 @@
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btn_CopyTxKey">
|
|
||||||
<property name="text">
|
|
||||||
<string>Copy</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Transaction Key</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="txProofWidget"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "ui_verifyproofdialog.h"
|
#include "ui_verifyproofdialog.h"
|
||||||
|
|
||||||
#include "libwalletqt/WalletManager.h"
|
#include "libwalletqt/WalletManager.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
@ -15,25 +16,34 @@ VerifyProofDialog::VerifyProofDialog(Wallet *wallet, QWidget *parent)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
m_success = QPixmap(":/assets/images/confirmed.png").scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
m_failure = QPixmap(":/assets/images/expired.png").scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
|
||||||
|
ui->frame_status->hide();
|
||||||
|
connect(ui->input_formattedProof, &QPlainTextEdit::textChanged, [this]{
|
||||||
|
ui->frame_status->hide();
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->btn_verify, &QPushButton::clicked, this, &VerifyProofDialog::checkProof);
|
connect(ui->btn_verify, &QPushButton::clicked, this, &VerifyProofDialog::checkProof);
|
||||||
|
connect(ui->btn_verifyFormattedProof, &QPushButton::clicked, this, &VerifyProofDialog::checkFormattedProof);
|
||||||
|
|
||||||
connect(ui->btn_clear, &QPushButton::clicked, [this]{
|
connect(ui->btn_clear, &QPushButton::clicked, [this]{
|
||||||
switch (ui->tabWidget->currentIndex()) {
|
switch (ui->tabWidget->currentIndex()) {
|
||||||
case 0:
|
case 0:
|
||||||
ui->lineEdit_spendTxID->clear();
|
ui->lineEdit_spendTxID->clear();
|
||||||
ui->lineEdit_spendMessage->clear();
|
ui->input_SpendMessage->clear();
|
||||||
ui->input_SpendProof->clear();
|
ui->input_SpendProof->clear();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ui->lineEdit_outTxID->clear();
|
ui->lineEdit_outTxID->clear();
|
||||||
ui->lineEdit_outAddress->clear();
|
ui->lineEdit_outAddress->clear();
|
||||||
ui->lineEdit_outMessage->clear();
|
ui->input_OutMessage->clear();
|
||||||
ui->input_OutProof->clear();
|
ui->input_OutProof->clear();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ui->lineEdit_inTxID->clear();
|
ui->lineEdit_inTxID->clear();
|
||||||
ui->lineEdit_inAddress->clear();
|
ui->lineEdit_inAddress->clear();
|
||||||
ui->lineEdit_inMessage->clear();
|
ui->input_inMessage->clear();
|
||||||
ui->input_InProof->clear();
|
ui->input_InProof->clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +58,7 @@ VerifyProofDialog::~VerifyProofDialog()
|
||||||
void VerifyProofDialog::checkProof() {
|
void VerifyProofDialog::checkProof() {
|
||||||
switch (ui->tabWidget->currentIndex()) {
|
switch (ui->tabWidget->currentIndex()) {
|
||||||
case 0:
|
case 0:
|
||||||
this->checkSpendProof();
|
this->checkSpendProof(ui->lineEdit_spendTxID->text(), ui->input_SpendMessage->toPlainText(), ui->input_SpendProof->toPlainText());
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
this->checkOutProof();
|
this->checkOutProof();
|
||||||
|
@ -59,40 +69,105 @@ void VerifyProofDialog::checkProof() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifyProofDialog::checkSpendProof() {
|
|
||||||
auto r = m_wallet->checkSpendProof(ui->lineEdit_spendTxID->text(), ui->lineEdit_spendMessage->text(), ui->input_SpendProof->toPlainText());
|
|
||||||
|
|
||||||
if (!r.first) {
|
|
||||||
QMessageBox::information(this, "Information", m_wallet->errorString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.second ? QMessageBox::information(this, "Information", "Proof is valid")
|
|
||||||
: QMessageBox::warning(this, "Warning", "Proof is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
void VerifyProofDialog::checkOutProof() {
|
|
||||||
this->checkTxProof(ui->lineEdit_outTxID->text(), ui->lineEdit_outAddress->text(), ui->lineEdit_outMessage->text(), ui->input_OutProof->toPlainText());
|
|
||||||
}
|
|
||||||
|
|
||||||
void VerifyProofDialog::checkInProof() {
|
|
||||||
this->checkTxProof(ui->lineEdit_inTxID->text(), ui->lineEdit_inAddress->text(), ui->lineEdit_inMessage->text(), ui->input_InProof->toPlainText());
|
|
||||||
}
|
|
||||||
|
|
||||||
void VerifyProofDialog::checkTxProof(const QString &txId, const QString &address, const QString &message,
|
void VerifyProofDialog::checkTxProof(const QString &txId, const QString &address, const QString &message,
|
||||||
const QString &signature) {
|
const QString &signature) {
|
||||||
TxProofResult r = m_wallet->checkTxProof(txId, address, message, signature);
|
TxProofResult r = m_wallet->checkTxProof(txId, address, message, signature);
|
||||||
|
|
||||||
if (!r.success) {
|
if (!r.success) {
|
||||||
QMessageBox::information(this, "Information", m_wallet->errorString());
|
this->proofStatus(false, m_wallet->errorString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!r.good) {
|
if (!r.good) {
|
||||||
QMessageBox::warning(this, "Warning", "Proof is invalid");
|
this->proofStatus(false, "Proof is invalid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString msg = QString("This address received %1 monero, with %2 confirmation(s)").arg(WalletManager::displayAmount(r.received), QString::number(r.confirmations));
|
this->proofStatus(true, QString("Proof is valid.\n\nThis address received %1 XMR, with %2 confirmation(s)").arg(WalletManager::displayAmount(r.received), QString::number(r.confirmations)));
|
||||||
QMessageBox::information(this, "Information", QString("Proof is valid.\n\n%1").arg(msg));
|
}
|
||||||
|
|
||||||
|
void VerifyProofDialog::checkSpendProof(const QString &txId, const QString &message, const QString &signature) {
|
||||||
|
auto r = m_wallet->checkSpendProof(txId, message, signature);
|
||||||
|
|
||||||
|
if (!r.first) {
|
||||||
|
this->proofStatus(false, m_wallet->errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.second ? this->proofStatus(true, "Proof is valid")
|
||||||
|
: this->proofStatus(false, "Proof is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyProofDialog::checkOutProof() {
|
||||||
|
this->checkTxProof(ui->lineEdit_outTxID->text(), ui->lineEdit_outAddress->text(), ui->input_OutMessage->toPlainText(), ui->input_OutProof->toPlainText());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyProofDialog::checkInProof() {
|
||||||
|
this->checkTxProof(ui->lineEdit_inTxID->text(), ui->lineEdit_inAddress->text(), ui->input_inMessage->toPlainText(), ui->input_InProof->toPlainText());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyProofDialog::checkFormattedProof() {
|
||||||
|
QRegularExpression proof("-----BEGIN (?<type>\\w+)-----\\n"
|
||||||
|
"Network: (?<coin>\\w+) (?<network>\\w+)\\n"
|
||||||
|
"Txid: (?<txid>[0-9a-f]{64})\\n"
|
||||||
|
"(Address: (?<address>\\w+)\\n)?"
|
||||||
|
"\\n?"
|
||||||
|
"(?<message>.*?)\\n"
|
||||||
|
"-----BEGIN \\1 SIGNATURE-----\\n"
|
||||||
|
"\\n?"
|
||||||
|
"(?<signature>.*?)\\n"
|
||||||
|
"-----END \\1 SIGNATURE-----",
|
||||||
|
QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
|
||||||
|
|
||||||
|
QString formattedProof = ui->input_formattedProof->toPlainText();
|
||||||
|
QRegularExpressionMatch match = proof.match(formattedProof);
|
||||||
|
|
||||||
|
if (!match.hasMatch()) {
|
||||||
|
this->proofStatus(false, "Unable to parse proof");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString type = match.captured("type").toLower();
|
||||||
|
QString coin = match.captured("coin").toLower();
|
||||||
|
QString network = match.captured("network").toLower();
|
||||||
|
QString txid = match.captured("txid");
|
||||||
|
QString address = match.captured("address");
|
||||||
|
QString message = match.captured("message");
|
||||||
|
QString signature = match.captured("signature").remove('\n');
|
||||||
|
|
||||||
|
QStringList validTypes = {"inproof", "spendproof", "outproof"};
|
||||||
|
if (!validTypes.contains(type)) {
|
||||||
|
this->proofStatus(false, QString("Unknown proof type: %1").arg(type));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coin != "monero") {
|
||||||
|
this->proofStatus(false, QString("Can't verify proof for coin: %1").arg(coin));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString walletNetwork = Utils::QtEnumToString(m_wallet->nettype()).toLower();
|
||||||
|
if (network != walletNetwork) {
|
||||||
|
this->proofStatus(false, QString("Can't verify proof for %1 network when %2 wallet is opened").arg(network, walletNetwork));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == "outproof" || type == "inproof") {
|
||||||
|
this->checkTxProof(txid, address, message, signature);
|
||||||
|
}
|
||||||
|
if (type == "spendproof") {
|
||||||
|
this->checkSpendProof(txid, message, signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyProofDialog::proofStatus(bool success, const QString &message) {
|
||||||
|
if (ui->tabWidget_proofFormat->currentIndex() == 0) {
|
||||||
|
ui->frame_status->show();
|
||||||
|
ui->label_icon->setPixmap(success ? m_success : m_failure);
|
||||||
|
ui->label_status->setText(message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
success ? QMessageBox::information(this, "Information", message)
|
||||||
|
: QMessageBox::warning(this, "Warning", message);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
#define FEATHER_VERIFYPROOFDIALOG_H
|
#define FEATHER_VERIFYPROOFDIALOG_H
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QIcon>
|
||||||
#include "libwalletqt/Wallet.h"
|
#include "libwalletqt/Wallet.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -24,9 +25,14 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void checkTxProof(const QString &txId, const QString &address, const QString &message, const QString &signature);
|
void checkTxProof(const QString &txId, const QString &address, const QString &message, const QString &signature);
|
||||||
void checkSpendProof();
|
void checkSpendProof(const QString &txId, const QString &message, const QString &signature);
|
||||||
void checkOutProof();
|
void checkOutProof();
|
||||||
void checkInProof();
|
void checkInProof();
|
||||||
|
void checkFormattedProof();
|
||||||
|
void proofStatus(bool success, const QString &message);
|
||||||
|
|
||||||
|
QPixmap m_success;
|
||||||
|
QPixmap m_failure;
|
||||||
|
|
||||||
Ui::VerifyProofDialog *ui;
|
Ui::VerifyProofDialog *ui;
|
||||||
Wallet *m_wallet;
|
Wallet *m_wallet;
|
||||||
|
|
|
@ -6,14 +6,123 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1123</width>
|
<width>770</width>
|
||||||
<height>413</height>
|
<height>587</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Verify transaction proof</string>
|
<string>Verify transaction proof</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget_proofFormat">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab_formatted">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Formatted</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="text">
|
||||||
|
<string>Paste formatted proof here:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="input_formattedProof"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_status">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_icon">
|
||||||
|
<property name="text">
|
||||||
|
<string>icon</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>10</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_status">
|
||||||
|
<property name="text">
|
||||||
|
<string>Proof is valid</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_verifyFormattedProof">
|
||||||
|
<property name="text">
|
||||||
|
<string>Verify</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_manual">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Manual</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -24,7 +133,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>2</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="SpendProof">
|
<widget class="QWidget" name="SpendProof">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -57,13 +166,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLineEdit" name="lineEdit_spendMessage">
|
|
||||||
<property name="placeholderText">
|
|
||||||
<string>Optional message against which the signature is signed</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -81,6 +183,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="input_SpendMessage">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Optional message against which the signature is signed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -153,13 +262,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLineEdit" name="lineEdit_outMessage">
|
|
||||||
<property name="placeholderText">
|
|
||||||
<string>Optional message against which the signature is signed</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QPlainTextEdit" name="input_OutProof">
|
<widget class="QPlainTextEdit" name="input_OutProof">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
|
@ -167,6 +269,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="input_OutMessage">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Optional message against which the signature is signed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -212,13 +321,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_13">
|
|
||||||
<property name="text">
|
|
||||||
<string>Address:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="lineEdit_inAddress">
|
<widget class="QLineEdit" name="lineEdit_inAddress">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
|
@ -226,6 +328,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="input_InProof">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>InProof..</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="lineEdit_inTxID">
|
<widget class="QLineEdit" name="lineEdit_inTxID">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
|
@ -233,6 +342,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="input_inMessage">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Optional message against which the signature is signed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_15">
|
<widget class="QLabel" name="label_15">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -240,17 +356,10 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="1" column="0">
|
||||||
<widget class="QLineEdit" name="lineEdit_inMessage">
|
<widget class="QLabel" name="label_13">
|
||||||
<property name="placeholderText">
|
<property name="text">
|
||||||
<string>Optional message against which the signature is signed</string>
|
<string>Address:</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QPlainTextEdit" name="input_InProof">
|
|
||||||
<property name="placeholderText">
|
|
||||||
<string>InProof..</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -309,6 +418,10 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "historywidget.h"
|
#include "historywidget.h"
|
||||||
#include "ui_historywidget.h"
|
#include "ui_historywidget.h"
|
||||||
#include "dialog/transactioninfodialog.h"
|
#include "dialog/transactioninfodialog.h"
|
||||||
|
#include "dialog/TxProofDialog.h"
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
HistoryWidget::HistoryWidget(QWidget *parent)
|
HistoryWidget::HistoryWidget(QWidget *parent)
|
||||||
|
@ -20,6 +21,7 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
||||||
// copy menu
|
// copy menu
|
||||||
m_copyMenu->setIcon(QIcon(":/assets/images/copy.png"));
|
m_copyMenu->setIcon(QIcon(":/assets/images/copy.png"));
|
||||||
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
||||||
|
m_copyMenu->addAction("Description", this, [this]{copy(copyField::Description);});
|
||||||
m_copyMenu->addAction("Date", this, [this]{copy(copyField::Date);});
|
m_copyMenu->addAction("Date", this, [this]{copy(copyField::Date);});
|
||||||
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
|
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
|
||||||
|
|
||||||
|
@ -48,34 +50,29 @@ void HistoryWidget::showContextMenu(const QPoint &point) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenu menu(this);
|
QMenu menu(this);
|
||||||
TransactionInfo::Direction direction;
|
|
||||||
QString txid;
|
|
||||||
bool unconfirmed;
|
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&direction, &txid, &unconfirmed](TransactionInfo &tInfo) {
|
|
||||||
direction = tInfo.direction();
|
|
||||||
txid = tInfo.hash();
|
|
||||||
unconfirmed = tInfo.isFailed() || tInfo.isPending();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (AppContext::txCache.contains(txid) && unconfirmed && direction != TransactionInfo::Direction_In) {
|
auto *tx = ui->history->currentEntry();
|
||||||
|
if (!tx) return;
|
||||||
|
|
||||||
|
bool unconfirmed = tx->isFailed() || tx->isPending();
|
||||||
|
if (AppContext::txCache.contains(tx->hash()) && unconfirmed && tx->direction() != TransactionInfo::Direction_In) {
|
||||||
menu.addAction(QIcon(":/assets/images/info.png"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
|
menu.addAction(QIcon(":/assets/images/info.png"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.addMenu(m_copyMenu);
|
menu.addMenu(m_copyMenu);
|
||||||
menu.addAction(QIcon(":/assets/images/info.png"), "Show details", this, &HistoryWidget::showTxDetails);
|
menu.addAction(QIcon(":/assets/images/info.png"), "Show details", this, &HistoryWidget::showTxDetails);
|
||||||
menu.addAction(QIcon(":/assets/images/network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
menu.addAction(QIcon(":/assets/images/network.png"), "View on block explorer", this, &HistoryWidget::onViewOnBlockExplorer);
|
||||||
|
menu.addAction("Create tx proof", this, &HistoryWidget::createTxProof);
|
||||||
|
|
||||||
menu.exec(ui->history->viewport()->mapToGlobal(point));
|
menu.exec(ui->history->viewport()->mapToGlobal(point));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onResendTransaction() {
|
void HistoryWidget::onResendTransaction() {
|
||||||
QModelIndex index = ui->history->currentIndex();
|
auto *tx = ui->history->currentEntry();
|
||||||
QString txid;
|
if (tx) {
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&txid](TransactionInfo &tInfo) {
|
QString txid = tx->hash();
|
||||||
txid = tInfo.hash();
|
|
||||||
});
|
|
||||||
|
|
||||||
emit resendTransaction(txid);
|
emit resendTransaction(txid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
|
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
|
||||||
|
@ -83,39 +80,35 @@ void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet
|
||||||
m_model = model;
|
m_model = model;
|
||||||
m_wallet = wallet;
|
m_wallet = wallet;
|
||||||
m_txHistory = m_wallet->history();
|
m_txHistory = m_wallet->history();
|
||||||
ui->history->setModel(m_model);
|
ui->history->setHistoryModel(m_model);
|
||||||
|
m_wallet->transactionHistoryModel()->amountPrecision = config()->get(Config::amountPrecision).toInt();
|
||||||
|
|
||||||
ui->history->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
// Load view state
|
||||||
ui->history->header()->setSectionResizeMode(TransactionHistoryModel::Description, QHeaderView::Stretch);
|
ui->history->setViewState(QByteArray::fromBase64(config()->get(Config::GUI_HistoryViewState).toByteArray()));
|
||||||
ui->history->hideColumn(TransactionHistoryModel::TxID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::resetModel()
|
void HistoryWidget::resetModel()
|
||||||
{
|
{
|
||||||
|
// Save view state
|
||||||
|
config()->set(Config::GUI_HistoryViewState, ui->history->viewState().toBase64());
|
||||||
|
config()->sync();
|
||||||
|
|
||||||
ui->history->setModel(nullptr);
|
ui->history->setModel(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::showTxDetails() {
|
void HistoryWidget::showTxDetails() {
|
||||||
QModelIndex index = ui->history->currentIndex();
|
auto *tx = ui->history->currentEntry();
|
||||||
|
if (!tx) return;
|
||||||
|
|
||||||
TransactionInfo *i = nullptr;
|
auto *dialog = new TransactionInfoDialog(m_wallet, tx, this);
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&i](TransactionInfo &tInfo) {
|
dialog->show();
|
||||||
i = &tInfo;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (i != nullptr) {
|
|
||||||
auto * dialog = new TransactionInfoDialog(m_wallet, i, this);
|
|
||||||
dialog->exec();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onViewOnBlockExplorer() {
|
void HistoryWidget::onViewOnBlockExplorer() {
|
||||||
QModelIndex index = ui->history->currentIndex();
|
auto *tx = ui->history->currentEntry();
|
||||||
|
if (!tx) return;
|
||||||
|
|
||||||
QString txid;
|
QString txid = tx->hash();
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&txid](TransactionInfo &tInfo) {
|
|
||||||
txid = tInfo.hash();
|
|
||||||
});
|
|
||||||
emit viewOnBlockExplorer(txid);
|
emit viewOnBlockExplorer(txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,27 +117,36 @@ void HistoryWidget::setSearchText(const QString &text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::setSearchFilter(const QString &filter) {
|
void HistoryWidget::setSearchFilter(const QString &filter) {
|
||||||
if(!m_model) return;
|
if (!m_model) return;
|
||||||
m_model->setSearchFilter(filter);
|
m_model->setSearchFilter(filter);
|
||||||
|
ui->history->setSearchMode(!filter.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::createTxProof() {
|
||||||
|
auto *tx = ui->history->currentEntry();
|
||||||
|
if (!tx) return;
|
||||||
|
|
||||||
|
auto *dialog = new TxProofDialog(this, m_wallet, tx);
|
||||||
|
dialog->exec();
|
||||||
|
dialog->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::copy(copyField field) {
|
void HistoryWidget::copy(copyField field) {
|
||||||
QModelIndex index = ui->history->currentIndex();
|
auto *tx = ui->history->currentEntry();
|
||||||
|
if (!tx) return;
|
||||||
|
|
||||||
QString data;
|
QString data = [field, tx]{
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [field, &data](TransactionInfo &tInfo) {
|
|
||||||
switch(field) {
|
switch(field) {
|
||||||
case copyField::TxID:
|
case copyField::TxID:
|
||||||
data = tInfo.hash();
|
return tx->hash();
|
||||||
break;
|
|
||||||
case copyField::Date:
|
case copyField::Date:
|
||||||
data = tInfo.timestamp().toString("yyyy-MM-dd HH:mm");
|
return tx->timestamp().toString("yyyy-MM-dd HH:mm");
|
||||||
break;
|
|
||||||
case copyField::Amount:
|
case copyField::Amount:
|
||||||
data = tInfo.displayAmount();
|
return tx->displayAmount();
|
||||||
break;
|
default:
|
||||||
|
return QString("");
|
||||||
}
|
}
|
||||||
});
|
}();
|
||||||
|
|
||||||
Utils::copyToClipboard(data);
|
Utils::copyToClipboard(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,10 +40,12 @@ private slots:
|
||||||
void onViewOnBlockExplorer();
|
void onViewOnBlockExplorer();
|
||||||
void setSearchFilter(const QString &filter);
|
void setSearchFilter(const QString &filter);
|
||||||
void onResendTransaction();
|
void onResendTransaction();
|
||||||
|
void createTxProof();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum copyField {
|
enum copyField {
|
||||||
TxID = 0,
|
TxID = 0,
|
||||||
|
Description,
|
||||||
Date,
|
Date,
|
||||||
Amount
|
Amount
|
||||||
};
|
};
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="history">
|
<widget class="HistoryView" name="history">
|
||||||
<property name="rootIsDecorated">
|
<property name="rootIsDecorated">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
@ -115,6 +115,13 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>HistoryView</class>
|
||||||
|
<extends>QTreeView</extends>
|
||||||
|
<header>model/HistoryView.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
|
@ -32,6 +32,15 @@ TransactionInfo* TransactionHistory::transaction(const QString &id)
|
||||||
return itr != m_tinfo.end() ? *itr : nullptr;
|
return itr != m_tinfo.end() ? *itr : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionInfo* TransactionHistory::transaction(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= m_tinfo.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_tinfo[index];
|
||||||
|
}
|
||||||
|
|
||||||
void TransactionHistory::refresh(quint32 accountIndex)
|
void TransactionHistory::refresh(quint32 accountIndex)
|
||||||
{
|
{
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
|
|
@ -29,6 +29,7 @@ class TransactionHistory : public QObject
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE bool transaction(int index, std::function<void (TransactionInfo &)> callback);
|
Q_INVOKABLE bool transaction(int index, std::function<void (TransactionInfo &)> callback);
|
||||||
Q_INVOKABLE TransactionInfo * transaction(const QString &id);
|
Q_INVOKABLE TransactionInfo * transaction(const QString &id);
|
||||||
|
TransactionInfo* transaction(int index);
|
||||||
Q_INVOKABLE void refresh(quint32 accountIndex);
|
Q_INVOKABLE void refresh(quint32 accountIndex);
|
||||||
Q_INVOKABLE void setTxNote(const QString &txid, const QString ¬e);
|
Q_INVOKABLE void setTxNote(const QString &txid, const QString ¬e);
|
||||||
Q_INVOKABLE bool writeCSV(const QString &path);
|
Q_INVOKABLE bool writeCSV(const QString &path);
|
||||||
|
|
|
@ -255,6 +255,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||||
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_balanceWidget, &TickerWidget::init);
|
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_balanceWidget, &TickerWidget::init);
|
||||||
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx, &AppContext::onPreferredFiatCurrencyChanged);
|
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx, &AppContext::onPreferredFiatCurrencyChanged);
|
||||||
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
|
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
|
||||||
|
connect(m_windowSettings, &Settings::amountPrecisionChanged, m_ctx, &AppContext::onAmountPrecisionChanged);
|
||||||
|
|
||||||
// Skin
|
// Skin
|
||||||
connect(m_windowSettings, &Settings::skinChanged, this, &MainWindow::skinChanged);
|
connect(m_windowSettings, &Settings::skinChanged, this, &MainWindow::skinChanged);
|
||||||
|
@ -584,9 +585,7 @@ void MainWindow::onWalletOpened() {
|
||||||
m_wizard->hide();
|
m_wizard->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->raise();
|
this->bringToFront();
|
||||||
this->show();
|
|
||||||
this->activateWindow();
|
|
||||||
this->setEnabled(true);
|
this->setEnabled(true);
|
||||||
if(!m_ctx->tor->torConnected)
|
if(!m_ctx->tor->torConnected)
|
||||||
this->setStatusText("Wallet opened - Starting Tor (may take a while)");
|
this->setStatusText("Wallet opened - Starting Tor (may take a while)");
|
||||||
|
@ -766,17 +765,10 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QVecto
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid) {
|
void MainWindow::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid) {
|
||||||
if(status) { // success
|
if (status) { // success
|
||||||
QString body = QString("Successfully sent %1 transaction(s).").arg(txid.count());
|
QString body = QString("Successfully sent %1 transaction(s).").arg(txid.count());
|
||||||
QMessageBox::information(this, "Transactions sent", body);
|
QMessageBox::information(this, "Transactions sent", body);
|
||||||
ui->sendWidget->clearFields();
|
ui->sendWidget->clearFields();
|
||||||
|
|
||||||
for(const auto &entry: txid) {
|
|
||||||
m_ctx->currentWallet->setUserNote(entry, m_ctx->tmpTxDescription);
|
|
||||||
AppContext::txDescriptionCache[entry] = m_ctx->tmpTxDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ctx->tmpTxDescription = "";
|
|
||||||
} else {
|
} else {
|
||||||
auto err = tx->errorString();
|
auto err = tx->errorString();
|
||||||
QString body = QString("Error committing transaction: %1").arg(err);
|
QString body = QString("Error committing transaction: %1").arg(err);
|
||||||
|
@ -1341,6 +1333,14 @@ QString MainWindow::statusDots() {
|
||||||
return QString(".").repeated(m_statusDots);
|
return QString(".").repeated(m_statusDots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::bringToFront() {
|
||||||
|
ensurePolished();
|
||||||
|
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow() {
|
MainWindow::~MainWindow() {
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,6 +172,7 @@ private:
|
||||||
void setStatusText(const QString &text, bool override = false, int timeout = 1000);
|
void setStatusText(const QString &text, bool override = false, int timeout = 1000);
|
||||||
void showBalanceDialog();
|
void showBalanceDialog();
|
||||||
QString statusDots();
|
QString statusDots();
|
||||||
|
void bringToFront();
|
||||||
|
|
||||||
WalletWizard *createWizard(WalletWizard::Page startPage);
|
WalletWizard *createWizard(WalletWizard::Page startPage);
|
||||||
|
|
||||||
|
|
188
src/model/HistoryView.cpp
Normal file
188
src/model/HistoryView.cpp
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020-2021, The Monero Project.
|
||||||
|
|
||||||
|
#include "HistoryView.h"
|
||||||
|
|
||||||
|
#include "TransactionHistoryProxyModel.h"
|
||||||
|
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
HistoryView::HistoryView(QWidget *parent)
|
||||||
|
: QTreeView(parent)
|
||||||
|
, m_model(nullptr)
|
||||||
|
, m_headerMenu(new QMenu(this))
|
||||||
|
{
|
||||||
|
setUniformRowHeights(true);
|
||||||
|
setRootIsDecorated(false);
|
||||||
|
setAlternatingRowColors(true);
|
||||||
|
setDragEnabled(true);
|
||||||
|
setSortingEnabled(true);
|
||||||
|
|
||||||
|
header()->setStretchLastSection(false);
|
||||||
|
header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
|
connect(header(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(showHeaderMenu(QPoint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::setHistoryModel(TransactionHistoryProxyModel *model) {
|
||||||
|
m_model = model;
|
||||||
|
m_model->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
m_model->setSortRole(Qt::UserRole);
|
||||||
|
m_model->setDynamicSortFilter(true);
|
||||||
|
m_model->setSortLocaleAware(true);
|
||||||
|
|
||||||
|
QTreeView::setModel(m_model);
|
||||||
|
resetViewToDefaults();
|
||||||
|
|
||||||
|
m_headerMenu->clear();
|
||||||
|
|
||||||
|
// Actions to toggle column visibility, each carrying the corresponding
|
||||||
|
// column index as data
|
||||||
|
m_columnActions = new QActionGroup(this);
|
||||||
|
m_columnActions->setExclusive(false);
|
||||||
|
for (int visualIndex = 1; visualIndex < header()->count(); ++visualIndex) {
|
||||||
|
int logicalIndex = header()->logicalIndex(visualIndex);
|
||||||
|
QString caption = m_model->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString();
|
||||||
|
if (caption.isEmpty()) {
|
||||||
|
caption = m_model->headerData(logicalIndex, Qt::Horizontal, Qt::ToolTipRole).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto action = m_headerMenu->addAction(caption);
|
||||||
|
action->setCheckable(true);
|
||||||
|
action->setData(logicalIndex);
|
||||||
|
m_columnActions->addAction(action);
|
||||||
|
}
|
||||||
|
connect(m_columnActions, &QActionGroup::triggered, this, &HistoryView::toggleColumnVisibility);
|
||||||
|
|
||||||
|
m_headerMenu->addSeparator();
|
||||||
|
m_headerMenu->addAction(tr("Fit to window"), this, &HistoryView::fitColumnsToWindow);
|
||||||
|
m_headerMenu->addAction(tr("Fit to contents"), this, &HistoryView::fitColumnsToContents);
|
||||||
|
m_headerMenu->addSeparator();
|
||||||
|
m_headerMenu->addAction(tr("Reset to defaults"), this, &HistoryView::resetViewToDefaults);
|
||||||
|
|
||||||
|
fitColumnsToWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionHistoryModel* HistoryView::sourceModel()
|
||||||
|
{
|
||||||
|
return dynamic_cast<TransactionHistoryModel *>(m_model->sourceModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionInfo* 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) {
|
||||||
|
m_inSearchMode = mode;
|
||||||
|
|
||||||
|
if (mode) {
|
||||||
|
header()->showSection(TransactionHistoryModel::TxID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HistoryView::viewState() const
|
||||||
|
{
|
||||||
|
return header()->saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HistoryView::setViewState(const QByteArray& state)
|
||||||
|
{
|
||||||
|
// Reset to unsorted first (https://bugreports.qt.io/browse/QTBUG-86694)
|
||||||
|
header()->setSortIndicator(-1, Qt::AscendingOrder);
|
||||||
|
bool status = header()->restoreState(state);
|
||||||
|
m_columnsNeedRelayout = state.isEmpty();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::showHeaderMenu(const QPoint& position)
|
||||||
|
{
|
||||||
|
const QList<QAction*> actions = m_columnActions->actions();
|
||||||
|
for (auto& action : actions) {
|
||||||
|
Q_ASSERT(static_cast<QMetaType::Type>(action->data().type()) == QMetaType::Int);
|
||||||
|
if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int columnIndex = action->data().toInt();
|
||||||
|
action->setChecked(!isColumnHidden(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_headerMenu->popup(mapToGlobal(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::toggleColumnVisibility(QAction* action)
|
||||||
|
{
|
||||||
|
// Verify action carries a column index as data. Since QVariant.toInt()
|
||||||
|
// below will accept anything that's interpretable as int, perform a type
|
||||||
|
// check here to make sure data actually IS int
|
||||||
|
Q_ASSERT(static_cast<QMetaType::Type>(action->data().type()) == QMetaType::Int);
|
||||||
|
if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle column visibility. Visible columns will only be hidden if at
|
||||||
|
// least one visible column remains, as the table header will disappear
|
||||||
|
// entirely when all columns are hidden
|
||||||
|
int columnIndex = action->data().toInt();
|
||||||
|
if (action->isChecked()) {
|
||||||
|
header()->showSection(columnIndex);
|
||||||
|
if (header()->sectionSize(columnIndex) == 0) {
|
||||||
|
header()->resizeSection(columnIndex, header()->defaultSectionSize());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((header()->count() - header()->hiddenSectionCount()) > 1) {
|
||||||
|
header()->hideSection(columnIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
action->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::fitColumnsToWindow()
|
||||||
|
{
|
||||||
|
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
header()->setSectionResizeMode(TransactionHistoryModel::Description, QHeaderView::Stretch);
|
||||||
|
header()->setStretchLastSection(false);
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::fitColumnsToContents()
|
||||||
|
{
|
||||||
|
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
header()->setSectionResizeMode(QHeaderView::Interactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryView::resetViewToDefaults()
|
||||||
|
{
|
||||||
|
if (m_inSearchMode) {
|
||||||
|
header()->showSection(TransactionHistoryModel::TxID);
|
||||||
|
} else {
|
||||||
|
header()->hideSection(TransactionHistoryModel::TxID);
|
||||||
|
}
|
||||||
|
header()->showSection(TransactionHistoryModel::Date);
|
||||||
|
header()->showSection(TransactionHistoryModel::Description);
|
||||||
|
header()->showSection(TransactionHistoryModel::Amount);
|
||||||
|
header()->showSection(TransactionHistoryModel::FiatAmount);
|
||||||
|
|
||||||
|
// Reset column order to logical indices
|
||||||
|
for (int i = 0; i < header()->count(); ++i) {
|
||||||
|
header()->moveSection(header()->visualIndex(i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_model->sort(TransactionHistoryModel::Date, Qt::DescendingOrder);
|
||||||
|
sortByColumn(TransactionHistoryModel::Date, Qt::DescendingOrder);
|
||||||
|
|
||||||
|
// The following call only relayouts reliably if the widget has been shown
|
||||||
|
// already, so only do it if the widget is visible and let showEvent() handle
|
||||||
|
// the initial default layout.
|
||||||
|
if (isVisible()) {
|
||||||
|
fitColumnsToWindow();
|
||||||
|
}
|
||||||
|
}
|
44
src/model/HistoryView.h
Normal file
44
src/model/HistoryView.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020-2021, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef FEATHER_HISTORYVIEW_H
|
||||||
|
#define FEATHER_HISTORYVIEW_H
|
||||||
|
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QActionGroup>
|
||||||
|
|
||||||
|
#include "TransactionHistoryModel.h"
|
||||||
|
|
||||||
|
class HistoryView : public QTreeView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HistoryView(QWidget* parent = nullptr);
|
||||||
|
void setHistoryModel(TransactionHistoryProxyModel *model);
|
||||||
|
TransactionInfo* currentEntry();
|
||||||
|
|
||||||
|
void setSearchMode(bool mode);
|
||||||
|
QByteArray viewState() const;
|
||||||
|
bool setViewState(const QByteArray& state);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void showHeaderMenu(const QPoint& position);
|
||||||
|
void toggleColumnVisibility(QAction* action);
|
||||||
|
void fitColumnsToWindow();
|
||||||
|
void fitColumnsToContents();
|
||||||
|
void resetViewToDefaults();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TransactionHistoryModel* sourceModel();
|
||||||
|
|
||||||
|
TransactionHistoryProxyModel* m_model;
|
||||||
|
bool m_inSearchMode = false;
|
||||||
|
bool m_columnsNeedRelayout = true;
|
||||||
|
|
||||||
|
QMenu* m_headerMenu;
|
||||||
|
QActionGroup* m_columnActions;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_HISTORYVIEW_H
|
|
@ -16,7 +16,7 @@ QString ModelUtils::displayAddress(const QString& address, int sections, const Q
|
||||||
for (int i = 0; i < sections; i += 1) {
|
for (int i = 0; i < sections; i += 1) {
|
||||||
list << address.mid(i*5, 5);
|
list << address.mid(i*5, 5);
|
||||||
}
|
}
|
||||||
list << "...";
|
list << "…"; // utf-8 Horizontal Ellipsis
|
||||||
for (int i = sections; i > 0; i -= 1) {
|
for (int i = sections; i > 0; i -= 1) {
|
||||||
list << address.mid(address.length() - i * 5, 5);
|
list << address.mid(address.length() - i * 5, 5);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "TransactionInfo.h"
|
#include "TransactionInfo.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "utils/ColorScheme.h"
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "ModelUtils.h"
|
||||||
|
|
||||||
TransactionHistoryModel::TransactionHistoryModel(QObject *parent)
|
TransactionHistoryModel::TransactionHistoryModel(QObject *parent)
|
||||||
: QAbstractTableModel(parent),
|
: QAbstractTableModel(parent),
|
||||||
|
@ -38,6 +39,11 @@ TransactionHistory *TransactionHistoryModel::transactionHistory() const {
|
||||||
return m_transactionHistory;
|
return m_transactionHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionInfo* TransactionHistoryModel::entryFromIndex(const QModelIndex &index) const {
|
||||||
|
Q_ASSERT(index.isValid() && index.row() < m_transactionHistory->count());
|
||||||
|
return m_transactionHistory->transaction(index.row());
|
||||||
|
}
|
||||||
|
|
||||||
int TransactionHistoryModel::rowCount(const QModelIndex &parent) const {
|
int TransactionHistoryModel::rowCount(const QModelIndex &parent) const {
|
||||||
if (parent.isValid()) {
|
if (parent.isValid()) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -65,8 +71,8 @@ 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 TransactionInfo &tInfo) {
|
||||||
if(role == Qt::DisplayRole || role == Qt::EditRole) {
|
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
|
||||||
result = parseTransactionInfo(tInfo, index.column());
|
result = parseTransactionInfo(tInfo, index.column(), role);
|
||||||
}
|
}
|
||||||
else if (role == Qt::TextAlignmentRole) {
|
else if (role == Qt::TextAlignmentRole) {
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
|
@ -130,31 +136,26 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tInfo, int column) const
|
QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const
|
||||||
{
|
{
|
||||||
switch (column)
|
switch (column)
|
||||||
{
|
{
|
||||||
case Column::Date:
|
case Column::Date:
|
||||||
return tInfo.timestamp().toString("yyyy-MM-dd HH:mm");
|
return tInfo.timestamp().toString("yyyy-MM-dd HH:mm ");
|
||||||
case Column::Description: {
|
case Column::Description:
|
||||||
// if this tx is still in the pool, then we wont get the
|
|
||||||
// description. We've cached it inside `AppContext::txDescriptionCache`
|
|
||||||
// for the time being.
|
|
||||||
if(tInfo.isPending()) {
|
|
||||||
auto hash = tInfo.hash();
|
|
||||||
if (AppContext::txDescriptionCache.contains(hash))
|
|
||||||
return AppContext::txDescriptionCache[hash];
|
|
||||||
}
|
|
||||||
return tInfo.description();
|
return tInfo.description();
|
||||||
}
|
|
||||||
case Column::Amount:
|
case Column::Amount:
|
||||||
{
|
{
|
||||||
QString amount = QString::number(tInfo.balanceDelta() / globals::cdiv, 'f', 4);
|
if (role == Qt::UserRole) {
|
||||||
|
return tInfo.balanceDelta();
|
||||||
|
}
|
||||||
|
QString amount = QString::number(tInfo.balanceDelta() / globals::cdiv, 'f', this->amountPrecision);
|
||||||
amount = (tInfo.direction() == TransactionInfo::Direction_Out) ? "-" + amount : "+" + amount;
|
amount = (tInfo.direction() == TransactionInfo::Direction_Out) ? "-" + amount : "+" + amount;
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
case Column::TxID:
|
case Column::TxID: {
|
||||||
return tInfo.hash();
|
return ModelUtils::displayAddress(tInfo.hash(), 1);
|
||||||
|
}
|
||||||
case Column::FiatAmount:
|
case Column::FiatAmount:
|
||||||
{
|
{
|
||||||
double usd_price = AppContext::txFiatHistory->get(tInfo.timestamp().toString("yyyyMMdd"));
|
double usd_price = AppContext::txFiatHistory->get(tInfo.timestamp().toString("yyyyMMdd"));
|
||||||
|
@ -164,8 +165,11 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
|
||||||
double usd_amount = usd_price * (tInfo.balanceDelta() / globals::cdiv);
|
double usd_amount = usd_price * (tInfo.balanceDelta() / globals::cdiv);
|
||||||
if(this->preferredFiatSymbol != "USD")
|
if(this->preferredFiatSymbol != "USD")
|
||||||
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
|
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
|
||||||
double fiat_rounded = ceil(Utils::roundSignificant(usd_amount, 3) * 100.0) / 100.0;
|
if (role == Qt::UserRole) {
|
||||||
|
return usd_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
double fiat_rounded = ceil(Utils::roundSignificant(usd_amount, 3) * 100.0) / 100.0;
|
||||||
return QString("%1").arg(Utils::amountToCurrencyString(fiat_rounded, this->preferredFiatSymbol));
|
return QString("%1").arg(Utils::amountToCurrencyString(fiat_rounded, this->preferredFiatSymbol));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -224,15 +228,10 @@ bool TransactionHistoryModel::setData(const QModelIndex &index, const QVariant &
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ItemFlags TransactionHistoryModel::flags(const QModelIndex &index) const {
|
Qt::ItemFlags TransactionHistoryModel::flags(const QModelIndex &index) const {
|
||||||
bool isPending;
|
|
||||||
m_transactionHistory->transaction(index.row(), [this, &isPending](const TransactionInfo &tInfo){
|
|
||||||
isPending = tInfo.isPending();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return Qt::ItemIsEnabled;
|
return Qt::ItemIsEnabled;
|
||||||
|
|
||||||
if (index.column() == Description && !isPending)
|
if (index.column() == Description)
|
||||||
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
|
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
|
||||||
|
|
||||||
return QAbstractTableModel::flags(index);
|
return QAbstractTableModel::flags(index);
|
||||||
|
|
|
@ -24,9 +24,9 @@ public:
|
||||||
enum Column
|
enum Column
|
||||||
{
|
{
|
||||||
Date = 0,
|
Date = 0,
|
||||||
|
TxID,
|
||||||
Description,
|
Description,
|
||||||
Amount,
|
Amount,
|
||||||
TxID,
|
|
||||||
FiatAmount,
|
FiatAmount,
|
||||||
COUNT
|
COUNT
|
||||||
};
|
};
|
||||||
|
@ -34,8 +34,11 @@ 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;
|
||||||
|
|
||||||
QString preferredFiatSymbol = "USD";
|
QString preferredFiatSymbol = "USD";
|
||||||
|
int amountPrecision = 4;
|
||||||
|
|
||||||
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;
|
||||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
||||||
|
@ -48,7 +51,7 @@ signals:
|
||||||
void transactionHistoryChanged();
|
void transactionHistoryChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariant parseTransactionInfo(const TransactionInfo &tInfo, int column) const;
|
QVariant parseTransactionInfo(const TransactionInfo &tInfo, int column, int role) const;
|
||||||
|
|
||||||
TransactionHistory * m_transactionHistory;
|
TransactionHistory * m_transactionHistory;
|
||||||
QIcon m_unconfirmedTx;
|
QIcon m_unconfirmedTx;
|
||||||
|
|
|
@ -16,6 +16,10 @@ TransactionHistoryProxyModel::TransactionHistoryProxyModel(Wallet *wallet, QObje
|
||||||
m_history = m_wallet->history();
|
m_history = m_wallet->history();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransactionHistory* TransactionHistoryProxyModel::history() {
|
||||||
|
return m_history;
|
||||||
|
}
|
||||||
|
|
||||||
bool TransactionHistoryProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
bool TransactionHistoryProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||||
{
|
{
|
||||||
QString description, txid, subaddrlabel;
|
QString description, txid, subaddrlabel;
|
||||||
|
|
|
@ -16,6 +16,7 @@ Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit TransactionHistoryProxyModel(Wallet *wallet, QObject* parent = nullptr);
|
explicit TransactionHistoryProxyModel(Wallet *wallet, QObject* parent = nullptr);
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||||
|
TransactionHistory* history();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setSearchFilter(const QString& searchString){
|
void setSearchFilter(const QString& searchString){
|
||||||
|
|
|
@ -43,9 +43,15 @@ Settings::Settings(QWidget *parent) :
|
||||||
if (m_skins.contains(settingsSkin))
|
if (m_skins.contains(settingsSkin))
|
||||||
ui->comboBox_skin->setCurrentIndex(m_skins.indexOf(settingsSkin));
|
ui->comboBox_skin->setCurrentIndex(m_skins.indexOf(settingsSkin));
|
||||||
|
|
||||||
|
for (int i = 0; i <= 12; i++) {
|
||||||
|
ui->comboBox_amountPrecision->addItem(QString::number(i));
|
||||||
|
}
|
||||||
|
ui->comboBox_amountPrecision->setCurrentIndex(config()->get(Config::amountPrecision).toInt());
|
||||||
|
|
||||||
connect(ui->comboBox_skin, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_skinChanged);
|
connect(ui->comboBox_skin, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_skinChanged);
|
||||||
connect(ui->comboBox_blockExplorer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_blockExplorerChanged);
|
connect(ui->comboBox_blockExplorer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_blockExplorerChanged);
|
||||||
connect(ui->comboBox_redditFrontend, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_redditFrontendChanged);
|
connect(ui->comboBox_redditFrontend, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_redditFrontendChanged);
|
||||||
|
connect(ui->comboBox_amountPrecision, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Settings::comboBox_amountPrecisionChanged);
|
||||||
|
|
||||||
// setup preferred fiat currency combobox
|
// setup preferred fiat currency combobox
|
||||||
QStringList fiatCurrencies;
|
QStringList fiatCurrencies;
|
||||||
|
@ -100,6 +106,11 @@ void Settings::comboBox_redditFrontendChanged(int pos) {
|
||||||
config()->set(Config::redditFrontend, redditFrontend);
|
config()->set(Config::redditFrontend, redditFrontend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Settings::comboBox_amountPrecisionChanged(int pos) {
|
||||||
|
config()->set(Config::amountPrecision, pos);
|
||||||
|
emit amountPrecisionChanged(pos);
|
||||||
|
}
|
||||||
|
|
||||||
void Settings::copyToClipboard() {
|
void Settings::copyToClipboard() {
|
||||||
ui->textLogs->copy();
|
ui->textLogs->copy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ signals:
|
||||||
void skinChanged(QString skinName);
|
void skinChanged(QString skinName);
|
||||||
void showHomeCCS(bool);
|
void showHomeCCS(bool);
|
||||||
void blockExplorerChanged(QString blockExplorer);
|
void blockExplorerChanged(QString blockExplorer);
|
||||||
|
void amountPrecisionChanged(int precision);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updatePaths();
|
void updatePaths();
|
||||||
|
@ -38,6 +39,7 @@ public slots:
|
||||||
void comboBox_skinChanged(int pos);
|
void comboBox_skinChanged(int pos);
|
||||||
void comboBox_blockExplorerChanged(int pos);
|
void comboBox_blockExplorerChanged(int pos);
|
||||||
void comboBox_redditFrontendChanged(int pos);
|
void comboBox_redditFrontendChanged(int pos);
|
||||||
|
void comboBox_amountPrecisionChanged(int pos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList m_skins{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"};
|
QStringList m_skins{"Native", "QDarkStyle", "Breeze/Dark", "Breeze/Light"};
|
||||||
|
|
|
@ -161,14 +161,14 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QCheckBox" name="checkBox_externalLink">
|
<widget class="QCheckBox" name="checkBox_externalLink">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Warn before opening external link</string>
|
<string>Warn before opening external link</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QCheckBox" name="checkBox_hideBalance">
|
<widget class="QCheckBox" name="checkBox_hideBalance">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Hide balance</string>
|
<string>Hide balance</string>
|
||||||
|
@ -201,6 +201,16 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>Amount precision:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QComboBox" name="comboBox_amountPrecision"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_node">
|
<widget class="QWidget" name="tab_node">
|
||||||
|
|
|
@ -46,7 +46,9 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||||
{Config::firstRun,{QS("firstRun"), false}},
|
{Config::firstRun,{QS("firstRun"), false}},
|
||||||
{Config::hideBalance, {QS("hideBalance"), false}},
|
{Config::hideBalance, {QS("hideBalance"), false}},
|
||||||
{Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}},
|
{Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}},
|
||||||
{Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}}
|
{Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}},
|
||||||
|
{Config::GUI_HistoryViewState, {QS("GUI_HistoryViewState"), {}}},
|
||||||
|
{Config::amountPrecision, {QS("amountPrecision"), 4}}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,9 @@ public:
|
||||||
firstRun,
|
firstRun,
|
||||||
hideBalance,
|
hideBalance,
|
||||||
redditFrontend,
|
redditFrontend,
|
||||||
showHistorySyncNotice
|
showHistorySyncNotice,
|
||||||
|
GUI_HistoryViewState,
|
||||||
|
amountPrecision
|
||||||
};
|
};
|
||||||
|
|
||||||
~Config() override;
|
~Config() override;
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "txproofwidget.h"
|
|
||||||
#include "ui_txproofwidget.h"
|
|
||||||
|
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
#include "utils/utils.h"
|
|
||||||
|
|
||||||
TxProofWidget::TxProofWidget(QWidget *parent, Wallet *wallet, TransactionInfo *txInfo)
|
|
||||||
: QWidget(parent)
|
|
||||||
, ui(new Ui::TxProofWidget)
|
|
||||||
, m_wallet(wallet)
|
|
||||||
, m_txid(txInfo->hash())
|
|
||||||
{
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
if (txInfo->direction() == TransactionInfo::Direction_Out) {
|
|
||||||
for (auto const &d: txInfo->destinations()) {
|
|
||||||
ui->comboBox_TxProofAddresses->addItem(d);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ui->btn_copySpendProof->setEnabled(false);
|
|
||||||
|
|
||||||
for (auto const &s: txInfo->subaddrIndex()) {
|
|
||||||
ui->comboBox_TxProofAddresses->addItem(wallet->address(txInfo->subaddrAccount(), s));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ui->comboBox_TxProofAddresses->count() == 0) {
|
|
||||||
ui->btn_copyTxProof->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(ui->btn_copySpendProof, &QPushButton::clicked, this, &TxProofWidget::copySpendProof);
|
|
||||||
connect(ui->btn_copyTxProof, &QPushButton::clicked, this, &TxProofWidget::copyTxProof);
|
|
||||||
|
|
||||||
ui->label_SpendProof->setHelpText("A SpendProof proves authorship of a transaction.\n\n"
|
|
||||||
"SpendProofs do not prove that a particular amount was sent to an address, for that use OutProofs.");
|
|
||||||
ui->label_InOutProof->setHelpText("An InProof proves ownership of an output.\n"
|
|
||||||
"An OutProof shows the prover sent an output to an address.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TxProofWidget::copySpendProof() {
|
|
||||||
auto txproof = m_wallet->getSpendProof(m_txid, "");
|
|
||||||
if (!txproof.error.isEmpty()) {
|
|
||||||
QMessageBox::warning(this, "Copy SpendProof", QString("Failed to copy SpendProof").arg(txproof.error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils::copyToClipboard(txproof.proof);
|
|
||||||
QMessageBox::information(this, "Copy SpendProof", "SpendProof copied to clipboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TxProofWidget::copyTxProof() {
|
|
||||||
auto txproof = m_wallet->getTxProof(m_txid, ui->comboBox_TxProofAddresses->currentText(), "");
|
|
||||||
if (!txproof.error.isEmpty()) {
|
|
||||||
QMessageBox::warning(this, "Copy Transaction Proof", QString("Failed to copy transaction proof: %1").arg(txproof.error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils::copyToClipboard(txproof.proof);
|
|
||||||
QMessageBox::information(this, "Copy Transaction Proof", "Transaction proof copied to clipboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
TxProofWidget::~TxProofWidget() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2020-2021, The Monero Project.
|
|
||||||
|
|
||||||
#ifndef FEATHER_TXPROOFWIDGET_H
|
|
||||||
#define FEATHER_TXPROOFWIDGET_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "libwalletqt/Wallet.h"
|
|
||||||
#include "libwalletqt/TransactionInfo.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class TxProofWidget;
|
|
||||||
}
|
|
||||||
|
|
||||||
class TxProofWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TxProofWidget(QWidget *parent, Wallet *wallet, TransactionInfo *txid);
|
|
||||||
~TxProofWidget() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void copySpendProof();
|
|
||||||
void copyTxProof();
|
|
||||||
|
|
||||||
Ui::TxProofWidget *ui;
|
|
||||||
QString m_txid;
|
|
||||||
Wallet *m_wallet;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //FEATHER_TXPROOFWIDGET_H
|
|
|
@ -1,110 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>TxProofWidget</class>
|
|
||||||
<widget class="QWidget" name="TxProofWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>647</width>
|
|
||||||
<height>79</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btn_copySpendProof">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Copy</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="HelpLabel" name="label_SpendProof">
|
|
||||||
<property name="text">
|
|
||||||
<string>SpendProof</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btn_copyTxProof">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Copy</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="HelpLabel" name="label_InOutProof">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>In/OutProof</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="comboBox_TxProofAddresses">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>HelpLabel</class>
|
|
||||||
<extends>QLabel</extends>
|
|
||||||
<header>components.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
Loading…
Reference in a new issue