mirror of
https://github.com/feather-wallet/feather.git
synced 2025-01-09 12:29:44 +00:00
Allow resending failed transactions
This commit is contained in:
parent
15211ce5fa
commit
169d14ab3a
10 changed files with 68 additions and 40 deletions
|
@ -37,6 +37,7 @@ 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::txDescriptionCache;
|
||||||
|
QMap<QString, QString> AppContext::txCache;
|
||||||
|
|
||||||
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||||
this->network = new QNetworkAccessManager();
|
this->network = new QNetworkAccessManager();
|
||||||
|
|
|
@ -86,6 +86,7 @@ public:
|
||||||
static WalletKeysFilesModel *wallets;
|
static WalletKeysFilesModel *wallets;
|
||||||
static double balance;
|
static double balance;
|
||||||
static QMap<QString, QString> txDescriptionCache;
|
static QMap<QString, QString> txDescriptionCache;
|
||||||
|
static QMap<QString, QString> txCache;
|
||||||
static TxFiatHistory *txFiatHistory;
|
static TxFiatHistory *txFiatHistory;
|
||||||
|
|
||||||
// libwalletqt
|
// libwalletqt
|
||||||
|
@ -172,7 +173,6 @@ signals:
|
||||||
void setTitle(const QString &title); // set window title
|
void setTitle(const QString &title); // set window title
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sorry();
|
|
||||||
const unsigned int m_donationBoundary = 15;
|
const unsigned int m_donationBoundary = 15;
|
||||||
UtilsNetworking *m_utilsNetworkingNodes;
|
UtilsNetworking *m_utilsNetworkingNodes;
|
||||||
QTimer *m_storeTimer = new QTimer(this);
|
QTimer *m_storeTimer = new QTimer(this);
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
|
|
||||||
#include "broadcasttxdialog.h"
|
#include "broadcasttxdialog.h"
|
||||||
#include "ui_broadcasttxdialog.h"
|
#include "ui_broadcasttxdialog.h"
|
||||||
|
#include "utils/nodes.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx)
|
BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx, const QString &transactionHex)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, m_ctx(ctx)
|
, m_ctx(ctx)
|
||||||
, ui(new Ui::BroadcastTxDialog)
|
, ui(new Ui::BroadcastTxDialog)
|
||||||
|
@ -23,6 +24,10 @@ BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx)
|
||||||
|
|
||||||
connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse);
|
connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse);
|
||||||
|
|
||||||
|
if (!transactionHex.isEmpty()) {
|
||||||
|
ui->transaction->setPlainText(transactionHex);
|
||||||
|
}
|
||||||
|
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +38,7 @@ void BroadcastTxDialog::broadcastTx() {
|
||||||
QString node;
|
QString node;
|
||||||
if (ui->radio_useCustom->isChecked())
|
if (ui->radio_useCustom->isChecked())
|
||||||
node = ui->customNode->text();
|
node = ui->customNode->text();
|
||||||
else
|
else if (ui->radio_useDefault->isChecked())
|
||||||
node = m_ctx->nodes->connection().full;
|
node = m_ctx->nodes->connection().full;
|
||||||
|
|
||||||
if (!node.startsWith("http://"))
|
if (!node.startsWith("http://"))
|
||||||
|
|
|
@ -17,7 +17,7 @@ class BroadcastTxDialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BroadcastTxDialog(QWidget *parent, AppContext *ctx);
|
explicit BroadcastTxDialog(QWidget *parent, AppContext *ctx, const QString &transactionHex = "");
|
||||||
~BroadcastTxDialog() override;
|
~BroadcastTxDialog() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
|
@ -63,16 +63,6 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>All transactions are broadcast over Tor</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -51,6 +51,7 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
|
||||||
|
|
||||||
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
|
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
|
||||||
|
|
||||||
|
AppContext::txCache[tx->txid()[0]] = tx->signedTxToHex(0);
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,20 +25,9 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
||||||
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
||||||
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);});
|
||||||
auto spendProof = m_copyMenu->addAction("Spend proof", this, &HistoryWidget::getSpendProof);
|
|
||||||
|
|
||||||
ui->history->setContextMenuPolicy(Qt::CustomContextMenu);
|
ui->history->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
connect(ui->history, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
|
connect(ui->history, &QTreeView::customContextMenuRequested, this, &HistoryWidget::showContextMenu);
|
||||||
QModelIndex index = ui->history->indexAt(point);
|
|
||||||
if (index.isValid()) {
|
|
||||||
TransactionInfo::Direction direction;
|
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&direction](TransactionInfo &tInfo) {
|
|
||||||
direction = tInfo.direction();
|
|
||||||
});
|
|
||||||
spendProof->setVisible(direction == TransactionInfo::Direction_Out);
|
|
||||||
m_contextMenu->exec(ui->history->viewport()->mapToGlobal(point));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(ui->search, &QLineEdit::textChanged, this, &HistoryWidget::setSearchFilter);
|
connect(ui->search, &QLineEdit::textChanged, this, &HistoryWidget::setSearchFilter);
|
||||||
|
|
||||||
connect(ui->history, &QTreeView::doubleClicked, [this](QModelIndex index){
|
connect(ui->history, &QTreeView::doubleClicked, [this](QModelIndex index){
|
||||||
|
@ -50,6 +39,43 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::showContextMenu(const QPoint &point) {
|
||||||
|
QModelIndex index = ui->history->indexAt(point);
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
menu.addAction(QIcon(":/assets/images/info.png"), "Resend transaction", this, &HistoryWidget::onResendTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.addMenu(m_copyMenu);
|
||||||
|
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.exec(ui->history->viewport()->mapToGlobal(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::onResendTransaction() {
|
||||||
|
QModelIndex index = ui->history->currentIndex();
|
||||||
|
QString txid;
|
||||||
|
m_txHistory->transaction(m_model->mapToSource(index).row(), [&txid](TransactionInfo &tInfo) {
|
||||||
|
txid = tInfo.hash();
|
||||||
|
});
|
||||||
|
|
||||||
|
emit resendTransaction(txid);
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
|
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
|
||||||
{
|
{
|
||||||
m_model = model;
|
m_model = model;
|
||||||
|
@ -86,14 +112,6 @@ void HistoryWidget::onViewOnBlockExplorer() {
|
||||||
emit viewOnBlockExplorer(txid);
|
emit viewOnBlockExplorer(txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::getSpendProof() {
|
|
||||||
QModelIndex index = ui->history->currentIndex();
|
|
||||||
|
|
||||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [this](TransactionInfo &tInfo) {
|
|
||||||
emit spendProof(tInfo.hash());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void HistoryWidget::setSearchText(const QString &text) {
|
void HistoryWidget::setSearchText(const QString &text) {
|
||||||
ui->search->setText(text);
|
ui->search->setText(text);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,14 +29,14 @@ public slots:
|
||||||
void setSearchText(const QString &text);
|
void setSearchText(const QString &text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void spendProof(QString txid);
|
|
||||||
void viewOnBlockExplorer(QString txid);
|
void viewOnBlockExplorer(QString txid);
|
||||||
|
void resendTransaction(QString txid);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showTxDetails();
|
void showTxDetails();
|
||||||
void onViewOnBlockExplorer();
|
void onViewOnBlockExplorer();
|
||||||
void getSpendProof();
|
|
||||||
void setSearchFilter(const QString &filter);
|
void setSearchFilter(const QString &filter);
|
||||||
|
void onResendTransaction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum copyField {
|
enum copyField {
|
||||||
|
@ -46,6 +46,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
void copy(copyField field);
|
void copy(copyField field);
|
||||||
|
void showContextMenu(const QPoint &point);
|
||||||
|
|
||||||
Ui::HistoryWidget *ui;
|
Ui::HistoryWidget *ui;
|
||||||
QMenu *m_contextMenu;
|
QMenu *m_contextMenu;
|
||||||
|
|
|
@ -289,11 +289,8 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||||
});
|
});
|
||||||
|
|
||||||
// History
|
// History
|
||||||
connect(ui->historyWidget, &HistoryWidget::spendProof, [&](const QString &txid){
|
|
||||||
TxProof txproof = m_ctx->currentWallet->getSpendProof(txid, "");
|
|
||||||
Utils::copyToClipboard(txproof.proof);
|
|
||||||
});
|
|
||||||
connect(ui->historyWidget, &HistoryWidget::viewOnBlockExplorer, this, &MainWindow::onViewOnBlockExplorer);
|
connect(ui->historyWidget, &HistoryWidget::viewOnBlockExplorer, this, &MainWindow::onViewOnBlockExplorer);
|
||||||
|
connect(ui->historyWidget, &HistoryWidget::resendTransaction, this, &MainWindow::onResendTransaction);
|
||||||
|
|
||||||
// Contacts
|
// Contacts
|
||||||
connect(ui->contactWidget, &ContactsWidget::addContact, this, &MainWindow::onAddContact);
|
connect(ui->contactWidget, &ContactsWidget::addContact, this, &MainWindow::onAddContact);
|
||||||
|
@ -1055,6 +1052,20 @@ void MainWindow::onViewOnBlockExplorer(const QString &txid) {
|
||||||
Utils::externalLinkWarning(this, blockExplorerLink);
|
Utils::externalLinkWarning(this, blockExplorerLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::onResendTransaction(const QString &txid) {
|
||||||
|
if (!AppContext::txCache.contains(txid)) {
|
||||||
|
QMessageBox::warning(this, "Unable to resend transaction", "Transaction was not found in transaction cache. Unable to resend.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to a different node so chances of successful relay are higher
|
||||||
|
m_ctx->nodes->autoConnect(true);
|
||||||
|
|
||||||
|
auto dialog = new BroadcastTxDialog(this, m_ctx, AppContext::txCache[txid]);
|
||||||
|
dialog->exec();
|
||||||
|
dialog->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onAddContact(const QString &address, const QString &name) {
|
void MainWindow::onAddContact(const QString &address, const QString &name) {
|
||||||
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
|
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
|
||||||
if (!addressValid)
|
if (!addressValid)
|
||||||
|
|
|
@ -117,6 +117,7 @@ public slots:
|
||||||
void menuWalletOpenClicked();
|
void menuWalletOpenClicked();
|
||||||
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
||||||
void onViewOnBlockExplorer(const QString &txid);
|
void onViewOnBlockExplorer(const QString &txid);
|
||||||
|
void onResendTransaction(const QString &txid);
|
||||||
void onAddContact(const QString &address, const QString &name);
|
void onAddContact(const QString &address, const QString &name);
|
||||||
void importContacts();
|
void importContacts();
|
||||||
void showRestoreHeightDialog();
|
void showRestoreHeightDialog();
|
||||||
|
|
Loading…
Reference in a new issue