mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Merge #7707: [RPC][QT] UI support for abandoned transactions
8efed3b
[Qt] Support for abandoned/abandoning transactions (Jonas Schnelli)
This commit is contained in:
commit
a9149688f8
@ -268,7 +268,8 @@ RES_ICONS = \
|
|||||||
qt/res/icons/tx_output.png \
|
qt/res/icons/tx_output.png \
|
||||||
qt/res/icons/tx_mined.png \
|
qt/res/icons/tx_mined.png \
|
||||||
qt/res/icons/warning.png \
|
qt/res/icons/warning.png \
|
||||||
qt/res/icons/verify.png
|
qt/res/icons/verify.png \
|
||||||
|
qt/res/icons/transaction_abandoned.png
|
||||||
|
|
||||||
BITCOIN_QT_CPP = \
|
BITCOIN_QT_CPP = \
|
||||||
qt/bantablemodel.cpp \
|
qt/bantablemodel.cpp \
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
<file alias="fontbigger">res/icons/fontbigger.png</file>
|
<file alias="fontbigger">res/icons/fontbigger.png</file>
|
||||||
<file alias="fontsmaller">res/icons/fontsmaller.png</file>
|
<file alias="fontsmaller">res/icons/fontsmaller.png</file>
|
||||||
<file alias="prompticon">res/icons/chevron.png</file>
|
<file alias="prompticon">res/icons/chevron.png</file>
|
||||||
|
<file alias="transaction_abandoned">res/icons/transaction_abandoned.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/movies">
|
<qresource prefix="/movies">
|
||||||
<file alias="spinner-000">res/movies/spinner-000.png</file>
|
<file alias="spinner-000">res/movies/spinner-000.png</file>
|
||||||
|
@ -29,6 +29,8 @@ static const bool DEFAULT_SPLASHSCREEN = true;
|
|||||||
#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
|
#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
|
||||||
/* Transaction list -- TX status decoration - offline */
|
/* Transaction list -- TX status decoration - offline */
|
||||||
#define COLOR_TX_STATUS_OFFLINE QColor(192, 192, 192)
|
#define COLOR_TX_STATUS_OFFLINE QColor(192, 192, 192)
|
||||||
|
/* Transaction list -- TX status decoration - danger, tx needs attention */
|
||||||
|
#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100)
|
||||||
/* Transaction list -- TX status decoration - default color */
|
/* Transaction list -- TX status decoration - default color */
|
||||||
#define COLOR_BLACK QColor(0, 0, 0)
|
#define COLOR_BLACK QColor(0, 0, 0)
|
||||||
|
|
||||||
|
BIN
src/qt/res/icons/transaction_abandoned.png
Normal file
BIN
src/qt/res/icons/transaction_abandoned.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -39,7 +39,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
|
|||||||
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
|
else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
|
||||||
return tr("%1/offline").arg(nDepth);
|
return tr("%1/offline").arg(nDepth);
|
||||||
else if (nDepth == 0)
|
else if (nDepth == 0)
|
||||||
return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool")));
|
return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : "");
|
||||||
else if (nDepth < 6)
|
else if (nDepth < 6)
|
||||||
return tr("%1/unconfirmed").arg(nDepth);
|
return tr("%1/unconfirmed").arg(nDepth);
|
||||||
else
|
else
|
||||||
|
@ -239,6 +239,8 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
|
|||||||
else if (status.depth == 0)
|
else if (status.depth == 0)
|
||||||
{
|
{
|
||||||
status.status = TransactionStatus::Unconfirmed;
|
status.status = TransactionStatus::Unconfirmed;
|
||||||
|
if (wtx.isAbandoned())
|
||||||
|
status.status = TransactionStatus::Abandoned;
|
||||||
}
|
}
|
||||||
else if (status.depth < RecommendedNumConfirmations)
|
else if (status.depth < RecommendedNumConfirmations)
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
Unconfirmed, /**< Not yet mined into a block **/
|
Unconfirmed, /**< Not yet mined into a block **/
|
||||||
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
|
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
|
||||||
Conflicted, /**< Conflicts with other transaction or mempool **/
|
Conflicted, /**< Conflicts with other transaction or mempool **/
|
||||||
|
Abandoned, /**< Abandoned from the wallet **/
|
||||||
/// Generated (mined) transactions
|
/// Generated (mined) transactions
|
||||||
Immature, /**< Mined but waiting for maturity */
|
Immature, /**< Mined but waiting for maturity */
|
||||||
MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
|
MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
|
||||||
|
@ -312,6 +312,9 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
|
|||||||
case TransactionStatus::Unconfirmed:
|
case TransactionStatus::Unconfirmed:
|
||||||
status = tr("Unconfirmed");
|
status = tr("Unconfirmed");
|
||||||
break;
|
break;
|
||||||
|
case TransactionStatus::Abandoned:
|
||||||
|
status = tr("Abandoned");
|
||||||
|
break;
|
||||||
case TransactionStatus::Confirming:
|
case TransactionStatus::Confirming:
|
||||||
status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations);
|
status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations);
|
||||||
break;
|
break;
|
||||||
@ -468,6 +471,8 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
|
|||||||
return COLOR_TX_STATUS_OFFLINE;
|
return COLOR_TX_STATUS_OFFLINE;
|
||||||
case TransactionStatus::Unconfirmed:
|
case TransactionStatus::Unconfirmed:
|
||||||
return QIcon(":/icons/transaction_0");
|
return QIcon(":/icons/transaction_0");
|
||||||
|
case TransactionStatus::Abandoned:
|
||||||
|
return QIcon(":/icons/transaction_abandoned");
|
||||||
case TransactionStatus::Confirming:
|
case TransactionStatus::Confirming:
|
||||||
switch(wtx->status.depth)
|
switch(wtx->status.depth)
|
||||||
{
|
{
|
||||||
@ -573,6 +578,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
|
|||||||
case Qt::TextAlignmentRole:
|
case Qt::TextAlignmentRole:
|
||||||
return column_alignments[index.column()];
|
return column_alignments[index.column()];
|
||||||
case Qt::ForegroundRole:
|
case Qt::ForegroundRole:
|
||||||
|
// Use the "danger" color for abandoned transactions
|
||||||
|
if(rec->status.status == TransactionStatus::Abandoned)
|
||||||
|
{
|
||||||
|
return COLOR_TX_STATUS_DANGER;
|
||||||
|
}
|
||||||
// Non-confirmed (but not immature) as transactions are grey
|
// Non-confirmed (but not immature) as transactions are grey
|
||||||
if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature)
|
if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature)
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
|
TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
|
||||||
QWidget(parent), model(0), transactionProxyModel(0),
|
QWidget(parent), model(0), transactionProxyModel(0),
|
||||||
transactionView(0)
|
transactionView(0), abandonAction(0)
|
||||||
{
|
{
|
||||||
// Build filter row
|
// Build filter row
|
||||||
setContentsMargins(0,0,0,0);
|
setContentsMargins(0,0,0,0);
|
||||||
@ -137,6 +137,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
|
|||||||
transactionView = view;
|
transactionView = view;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
abandonAction = new QAction(tr("Abandon transaction"), this);
|
||||||
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
|
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
|
||||||
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
|
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
|
||||||
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
|
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
|
||||||
@ -153,8 +154,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
|
|||||||
contextMenu->addAction(copyTxIDAction);
|
contextMenu->addAction(copyTxIDAction);
|
||||||
contextMenu->addAction(copyTxHexAction);
|
contextMenu->addAction(copyTxHexAction);
|
||||||
contextMenu->addAction(copyTxPlainText);
|
contextMenu->addAction(copyTxPlainText);
|
||||||
contextMenu->addAction(editLabelAction);
|
|
||||||
contextMenu->addAction(showDetailsAction);
|
contextMenu->addAction(showDetailsAction);
|
||||||
|
contextMenu->addSeparator();
|
||||||
|
contextMenu->addAction(abandonAction);
|
||||||
|
contextMenu->addAction(editLabelAction);
|
||||||
|
|
||||||
mapperThirdPartyTxUrls = new QSignalMapper(this);
|
mapperThirdPartyTxUrls = new QSignalMapper(this);
|
||||||
|
|
||||||
@ -170,6 +173,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
|
|||||||
connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
|
connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
|
||||||
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
|
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
|
||||||
|
|
||||||
|
connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx()));
|
||||||
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
|
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
|
||||||
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
|
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
|
||||||
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
|
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
|
||||||
@ -360,12 +364,37 @@ void TransactionView::exportClicked()
|
|||||||
void TransactionView::contextualMenu(const QPoint &point)
|
void TransactionView::contextualMenu(const QPoint &point)
|
||||||
{
|
{
|
||||||
QModelIndex index = transactionView->indexAt(point);
|
QModelIndex index = transactionView->indexAt(point);
|
||||||
|
QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);
|
||||||
|
|
||||||
|
// check if transaction can be abandoned, disable context menu action in case it doesn't
|
||||||
|
uint256 hash;
|
||||||
|
hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
|
||||||
|
abandonAction->setEnabled(model->transactionCanBeAbandoned(hash));
|
||||||
|
|
||||||
if(index.isValid())
|
if(index.isValid())
|
||||||
{
|
{
|
||||||
contextMenu->exec(QCursor::pos());
|
contextMenu->exec(QCursor::pos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransactionView::abandonTx()
|
||||||
|
{
|
||||||
|
if(!transactionView || !transactionView->selectionModel())
|
||||||
|
return;
|
||||||
|
QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);
|
||||||
|
|
||||||
|
// get the hash from the TxHashRole (QVariant / QString)
|
||||||
|
uint256 hash;
|
||||||
|
QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString();
|
||||||
|
hash.SetHex(hashQStr.toStdString());
|
||||||
|
|
||||||
|
// Abandon the wallet transaction over the walletModel
|
||||||
|
model->abandonTransaction(hash);
|
||||||
|
|
||||||
|
// Update the table
|
||||||
|
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
|
||||||
|
}
|
||||||
|
|
||||||
void TransactionView::copyAddress()
|
void TransactionView::copyAddress()
|
||||||
{
|
{
|
||||||
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole);
|
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole);
|
||||||
|
@ -75,6 +75,7 @@ private:
|
|||||||
QFrame *dateRangeWidget;
|
QFrame *dateRangeWidget;
|
||||||
QDateTimeEdit *dateFrom;
|
QDateTimeEdit *dateFrom;
|
||||||
QDateTimeEdit *dateTo;
|
QDateTimeEdit *dateTo;
|
||||||
|
QAction *abandonAction;
|
||||||
|
|
||||||
QWidget *createDateRangeWidget();
|
QWidget *createDateRangeWidget();
|
||||||
|
|
||||||
@ -97,6 +98,7 @@ private Q_SLOTS:
|
|||||||
void copyTxPlainText();
|
void copyTxPlainText();
|
||||||
void openThirdPartyTxUrl(QString url);
|
void openThirdPartyTxUrl(QString url);
|
||||||
void updateWatchOnlyColumn(bool fHaveWatchOnly);
|
void updateWatchOnlyColumn(bool fHaveWatchOnly);
|
||||||
|
void abandonTx();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void doubleClicked(const QModelIndex&);
|
void doubleClicked(const QModelIndex&);
|
||||||
|
@ -668,3 +668,18 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t
|
|||||||
else
|
else
|
||||||
return wallet->AddDestData(dest, key, sRequest);
|
return wallet->AddDestData(dest, key, sRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WalletModel::transactionCanBeAbandoned(uint256 hash) const
|
||||||
|
{
|
||||||
|
LOCK2(cs_main, wallet->cs_wallet);
|
||||||
|
const CWalletTx *wtx = wallet->GetWalletTx(hash);
|
||||||
|
if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->InMempool())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WalletModel::abandonTransaction(uint256 hash) const
|
||||||
|
{
|
||||||
|
LOCK2(cs_main, wallet->cs_wallet);
|
||||||
|
return wallet->AbandonTransaction(hash);
|
||||||
|
}
|
||||||
|
@ -200,6 +200,9 @@ public:
|
|||||||
void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
|
void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
|
||||||
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
|
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
|
||||||
|
|
||||||
|
bool transactionCanBeAbandoned(uint256 hash) const;
|
||||||
|
bool abandonTransaction(uint256 hash) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CWallet *wallet;
|
CWallet *wallet;
|
||||||
bool fHaveWatchOnly;
|
bool fHaveWatchOnly;
|
||||||
|
Loading…
Reference in New Issue
Block a user