Add sent and received shielded Txs in the transactions tab.
This commit is contained in:
@@ -21,6 +21,7 @@ struct ToFields {
|
||||
struct Tx {
|
||||
QString fromAddr;
|
||||
QList<ToFields> toAddrs;
|
||||
double fee;
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QAbstractTableModel>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QObject>
|
||||
|
||||
86
src/rpc.cpp
86
src/rpc.cpp
@@ -1,7 +1,7 @@
|
||||
#include "rpc.h"
|
||||
#include "utils.h"
|
||||
#include "transactionitem.h"
|
||||
#include "settings.h"
|
||||
#include "senttxstore.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -35,7 +35,7 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
||||
// Set up the timer to watch for tx status
|
||||
txTimer = new QTimer(main);
|
||||
QObject::connect(txTimer, &QTimer::timeout, [=]() {
|
||||
refreshTxStatus();
|
||||
watchTxStatus();
|
||||
});
|
||||
// Start at every 10s. When an operation is pending, this will change to every second
|
||||
txTimer->start(Utils::updateSpeed);
|
||||
@@ -45,7 +45,7 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
||||
QObject::connect(priceTimer, &QTimer::timeout, [=]() {
|
||||
refreshZECPrice();
|
||||
});
|
||||
priceTimer->start(60 * 60 * 1000); // Every hour
|
||||
priceTimer->start(Utils::priceRefreshSpeed); // Every hour
|
||||
}
|
||||
|
||||
RPC::~RPC() {
|
||||
@@ -389,7 +389,7 @@ void RPC::getReceivedZTrans(QList<QString> zaddrs) {
|
||||
}
|
||||
}
|
||||
|
||||
transactionsTableModel->addNewData(txdata);
|
||||
transactionsTableModel->addZRecvData(txdata);
|
||||
|
||||
// Cleanup both responses;
|
||||
delete zaddrTxids;
|
||||
@@ -417,7 +417,7 @@ void RPC::getInfoThenRefresh() {
|
||||
|
||||
doRPC(payload, [=] (const json& reply) {
|
||||
// Testnet?
|
||||
if (reply.find("testnet") != reply.end()) {
|
||||
if (!reply["testnet"].is_null()) {
|
||||
Settings::getInstance()->setTestnet(reply["testnet"].get<json::boolean_t>());
|
||||
};
|
||||
|
||||
@@ -425,13 +425,11 @@ void RPC::getInfoThenRefresh() {
|
||||
QIcon i(":/icons/res/connected.png");
|
||||
main->statusIcon->setPixmap(i.pixmap(16, 16));
|
||||
|
||||
// Expect 2 data additions, then automatically refresh the table
|
||||
transactionsTableModel->prepNewData(2);
|
||||
|
||||
// Refresh everything.
|
||||
refreshBalances();
|
||||
refreshAddresses();
|
||||
refreshTransactions();
|
||||
refreshZSentTransactions();
|
||||
|
||||
// Call to see if the blockchain is syncing.
|
||||
json payload = {
|
||||
@@ -559,14 +557,14 @@ void RPC::refreshTransactions() {
|
||||
|
||||
for (auto& it : reply.get<json::array_t>()) {
|
||||
double fee = 0;
|
||||
if (it.find("fee") != it.end()) {
|
||||
if (!it["fee"].is_null()) {
|
||||
fee = it["fee"].get<json::number_float_t>();
|
||||
}
|
||||
|
||||
TransactionItem tx{
|
||||
QString::fromStdString(it["category"]),
|
||||
it["time"].get<json::number_unsigned_t>(),
|
||||
(it["address"].is_null() ? "(shielded)" : QString::fromStdString(it["address"])),
|
||||
(it["address"].is_null() ? "" : QString::fromStdString(it["address"])),
|
||||
QString::fromStdString(it["txid"]),
|
||||
it["amount"].get<json::number_float_t>() + fee,
|
||||
it["confirmations"].get<json::number_unsigned_t>()
|
||||
@@ -576,15 +574,55 @@ void RPC::refreshTransactions() {
|
||||
}
|
||||
|
||||
// Update model data, which updates the table view
|
||||
transactionsTableModel->addNewData(txdata);
|
||||
transactionsTableModel->addTData(txdata);
|
||||
});
|
||||
}
|
||||
|
||||
void RPC::refreshTxStatus(const QString& newOpid) {
|
||||
if (!newOpid.isEmpty()) {
|
||||
watchingOps.insert(newOpid);
|
||||
// Read sent Z transactions from the file.
|
||||
void RPC::refreshZSentTransactions() {
|
||||
auto sentZTxs = SentTxStore::readSentTxFile();
|
||||
QList<QString> txids;
|
||||
|
||||
for (auto sentTx: sentZTxs) {
|
||||
txids.push_back(sentTx.txid);
|
||||
}
|
||||
|
||||
// Look up all the txids to get the confirmation count for them.
|
||||
getBatchRPC(txids,
|
||||
[=] (QString txid) {
|
||||
json payload = {
|
||||
{"jsonrpc", "1.0"},
|
||||
{"id", "senttxid"},
|
||||
{"method", "gettransaction"},
|
||||
{"params", {txid.toStdString()}}
|
||||
};
|
||||
|
||||
return payload;
|
||||
},
|
||||
[=] (QMap<QString, json>* txidList) {
|
||||
auto newSentZTxs = sentZTxs;
|
||||
// Update the original sent list with the confirmation count
|
||||
// TODO: This whole thing is kinda inefficient. We should probably just update the file
|
||||
// with the confirmed block number, so we don't have to keep calling gettransaction for the
|
||||
// sent items.
|
||||
for (TransactionItem& sentTx: newSentZTxs) {
|
||||
auto error = txidList->value(sentTx.txid)["confirmations"].is_null();
|
||||
if (!error)
|
||||
sentTx.confirmations = txidList->value(sentTx.txid)["confirmations"].get<json::number_unsigned_t>();
|
||||
}
|
||||
|
||||
transactionsTableModel->addZSentData(newSentZTxs);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void RPC::addNewTxToWatch(Tx tx, const QString& newOpid) {
|
||||
watchingOps.insert(newOpid, tx);
|
||||
|
||||
watchTxStatus();
|
||||
}
|
||||
|
||||
void RPC::watchTxStatus() {
|
||||
// Make an RPC to load pending operation statues
|
||||
json payload = {
|
||||
{"jsonrpc", "1.0"},
|
||||
@@ -602,12 +640,13 @@ void RPC::refreshTxStatus(const QString& newOpid) {
|
||||
QString status = QString::fromStdString(it["status"]);
|
||||
if (status == "success") {
|
||||
auto txid = QString::fromStdString(it["result"]["txid"]);
|
||||
qDebug() << "Tx completed: " << txid;
|
||||
|
||||
SentTxStore::addToSentTx(watchingOps.value(id), txid);
|
||||
|
||||
main->ui->statusBar->showMessage(Utils::txidStatusMessage + " " + txid);
|
||||
main->loadingLabel->setVisible(false);
|
||||
|
||||
watchingOps.remove(id);
|
||||
txTimer->start(Utils::updateSpeed);
|
||||
|
||||
// Refresh balances to show unconfirmed balances
|
||||
refresh();
|
||||
@@ -622,17 +661,19 @@ void RPC::refreshTxStatus(const QString& newOpid) {
|
||||
main
|
||||
);
|
||||
|
||||
watchingOps.remove(id);
|
||||
txTimer->start(Utils::updateSpeed);
|
||||
watchingOps.remove(id);
|
||||
|
||||
main->ui->statusBar->showMessage(" Tx " % id % " failed", 15 * 1000);
|
||||
main->loadingLabel->setVisible(false);
|
||||
|
||||
msg.exec();
|
||||
} else if (status == "executing") {
|
||||
// If the operation is executing, then watch every second.
|
||||
txTimer->start(Utils::quickUpdateSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (watchingOps.isEmpty()) {
|
||||
txTimer->start(Utils::updateSpeed);
|
||||
} else {
|
||||
txTimer->start(Utils::quickUpdateSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,6 +687,7 @@ void RPC::refreshTxStatus(const QString& newOpid) {
|
||||
});
|
||||
}
|
||||
|
||||
// Get the ZEC->USD price from coinmarketcap using their API
|
||||
void RPC::refreshZECPrice() {
|
||||
QUrl cmcURL("https://api.coinmarketcap.com/v1/ticker/");
|
||||
|
||||
|
||||
16
src/rpc.h
16
src/rpc.h
@@ -11,6 +11,16 @@
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
||||
struct TransactionItem {
|
||||
QString type;
|
||||
unsigned long datetime;
|
||||
QString address;
|
||||
QString txid;
|
||||
double amount;
|
||||
unsigned long confirmations;
|
||||
};
|
||||
|
||||
class RPC
|
||||
{
|
||||
public:
|
||||
@@ -18,11 +28,12 @@ public:
|
||||
~RPC();
|
||||
|
||||
void refresh(); // Refresh all transactions
|
||||
void refreshTxStatus(const QString& newOpid = QString()); // Refresh the status of all pending txs.
|
||||
void refreshAddresses(); // Refresh wallet Z-addrs
|
||||
void refreshZECPrice();
|
||||
|
||||
void sendZTransaction (json params, const std::function<void(json)>& cb);
|
||||
void watchTxStatus();
|
||||
void addNewTxToWatch(Tx tx, const QString& newOpid);
|
||||
|
||||
BalancesTableModel* getBalancesModel() { return balancesTableModel; }
|
||||
const QList<QString>* getAllZAddresses() { return zaddresses; }
|
||||
@@ -40,6 +51,7 @@ private:
|
||||
|
||||
void refreshBalances();
|
||||
void refreshTransactions();
|
||||
void refreshZSentTransactions();
|
||||
|
||||
bool processUnspent (const json& reply);
|
||||
void updateUI (bool anyUnconfirmed);
|
||||
@@ -69,7 +81,7 @@ private:
|
||||
QMap<QString, double>* allBalances = nullptr;
|
||||
QList<QString>* zaddresses = nullptr;
|
||||
|
||||
QSet<QString> watchingOps;
|
||||
QMap<QString, Tx> watchingOps;
|
||||
|
||||
TxTableModel* transactionsTableModel = nullptr;
|
||||
BalancesTableModel* balancesTableModel = nullptr;
|
||||
|
||||
@@ -319,6 +319,7 @@ Tx MainWindow::createTxFromSendPage() {
|
||||
tx.toAddrs.push_back( ToFields{addr, amt, memo, memo.toUtf8().toHex()} );
|
||||
}
|
||||
|
||||
tx.fee = Utils::getMinerFee();
|
||||
return tx;
|
||||
}
|
||||
|
||||
@@ -416,7 +417,7 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
|
||||
minerFee->setObjectName(QStringLiteral("minerFee"));
|
||||
minerFee->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
|
||||
confirm.gridLayout->addWidget(minerFee, i, 1, 1, 1);
|
||||
minerFee->setText(Settings::getInstance()->getZECDisplayFormat(Utils::getMinerFee()));
|
||||
minerFee->setText(Settings::getInstance()->getZECDisplayFormat(tx.fee));
|
||||
|
||||
auto minerFeeUSD = new QLabel(confirm.sendToAddrs);
|
||||
QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
@@ -424,7 +425,7 @@ bool MainWindow::confirmTx(Tx tx, ToFields devFee) {
|
||||
minerFeeUSD->setObjectName(QStringLiteral("minerFeeUSD"));
|
||||
minerFeeUSD->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
|
||||
confirm.gridLayout->addWidget(minerFeeUSD, i, 2, 1, 1);
|
||||
minerFeeUSD->setText(Settings::getInstance()->getUSDFormat(Utils::getMinerFee()));
|
||||
minerFeeUSD->setText(Settings::getInstance()->getUSDFormat(tx.fee));
|
||||
|
||||
if (!devFee.addr.isEmpty()) {
|
||||
auto labelDevFee = new QLabel(confirm.sendToAddrs);
|
||||
@@ -517,7 +518,7 @@ void MainWindow::sendButton() {
|
||||
ui->statusBar->showMessage("Computing Tx: " % opid);
|
||||
|
||||
// And then start monitoring the transaction
|
||||
rpc->refreshTxStatus(opid);
|
||||
rpc->addNewTxToWatch(tx, opid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
86
src/senttxstore.cpp
Normal file
86
src/senttxstore.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "senttxstore.h"
|
||||
#include "settings.h"
|
||||
|
||||
/// Get the location of the app data file to be written.
|
||||
QString SentTxStore::writeableFile() {
|
||||
auto filename = QStringLiteral("senttxstore.dat");
|
||||
|
||||
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||
if (!dir.exists())
|
||||
QDir().mkpath(dir.absolutePath());
|
||||
|
||||
if (Settings::getInstance()->isTestnet()) {
|
||||
return dir.filePath("testnet-" % filename);
|
||||
} else {
|
||||
return dir.filePath(filename);
|
||||
}
|
||||
}
|
||||
|
||||
QList<TransactionItem> SentTxStore::readSentTxFile() {
|
||||
QFile data(writeableFile());
|
||||
if (!data.exists()) {
|
||||
return QList<TransactionItem>();
|
||||
}
|
||||
|
||||
QJsonDocument jsonDoc;
|
||||
|
||||
data.open(QFile::ReadOnly);
|
||||
jsonDoc = QJsonDocument().fromJson(data.readAll());
|
||||
data.close();
|
||||
|
||||
QList<TransactionItem> items;
|
||||
|
||||
for (auto i : jsonDoc.array()) {
|
||||
auto sentTx = i.toObject();
|
||||
TransactionItem t{"sent", (unsigned long)sentTx["datetime"].toInt(),
|
||||
sentTx["address"].toString(),
|
||||
sentTx["txid"].toString(), sentTx["amount"].toDouble(), 0};
|
||||
items.push_back(t);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
void SentTxStore::addToSentTx(Tx tx, QString txid) {
|
||||
QFile data(writeableFile());
|
||||
QJsonDocument jsonDoc;
|
||||
|
||||
// If data doesn't exist, then create a blank one
|
||||
if (!data.exists()) {
|
||||
QJsonArray a;
|
||||
jsonDoc.setArray(a);
|
||||
|
||||
QFile newFile(writeableFile());
|
||||
newFile.open(QFile::WriteOnly);
|
||||
newFile.write(jsonDoc.toJson());
|
||||
newFile.close();
|
||||
} else {
|
||||
data.open(QFile::ReadOnly);
|
||||
jsonDoc = QJsonDocument().fromJson(data.readAll());
|
||||
data.close();
|
||||
}
|
||||
|
||||
// Calculate total amount in this tx
|
||||
double totalAmount = 0;
|
||||
for (auto i : tx.toAddrs) {
|
||||
totalAmount += i.amount;
|
||||
}
|
||||
|
||||
auto list = jsonDoc.array();
|
||||
QJsonObject txItem;
|
||||
txItem["type"] = "sent";
|
||||
txItem["datetime"] = QDateTime().currentSecsSinceEpoch();
|
||||
txItem["address"] = QString(); // The sent address is blank, to be consistent with t-Addr sent behaviour
|
||||
txItem["txid"] = txid;
|
||||
txItem["amount"] = -totalAmount;
|
||||
txItem["fee"] = -tx.fee;
|
||||
list.append(txItem);
|
||||
|
||||
jsonDoc.setArray(list);
|
||||
|
||||
QFile writer(writeableFile());
|
||||
if (writer.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
writer.write(jsonDoc.toJson());
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
18
src/senttxstore.h
Normal file
18
src/senttxstore.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef SENTTXSTORE_H
|
||||
#define SENTTXSTORE_H
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "mainwindow.h"
|
||||
#include "rpc.h"
|
||||
|
||||
class SentTxStore {
|
||||
public:
|
||||
static QList<TransactionItem> readSentTxFile();
|
||||
static void addToSentTx(Tx tx, QString txid);
|
||||
|
||||
private:
|
||||
static QString writeableFile();
|
||||
|
||||
};
|
||||
|
||||
#endif // SENTTXSTORE_H
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef TRANSACTIONITEM_H
|
||||
#define TRANSACTIONITEM_H
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
struct TransactionItem {
|
||||
QString type;
|
||||
unsigned long datetime;
|
||||
QString address;
|
||||
QString txid;
|
||||
double amount;
|
||||
unsigned long confirmations;
|
||||
};
|
||||
|
||||
#endif // TRANSACTIONITEM_H
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "txtablemodel.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "rpc.h"
|
||||
|
||||
TxTableModel::TxTableModel(QObject *parent)
|
||||
: QAbstractTableModel(parent) {
|
||||
@@ -11,33 +12,46 @@ TxTableModel::~TxTableModel() {
|
||||
delete modeldata;
|
||||
}
|
||||
|
||||
void TxTableModel::prepNewData(int expect) {
|
||||
newmodeldata = new QList<TransactionItem>();
|
||||
expectedData = expect;
|
||||
void TxTableModel::addZSentData(const QList<TransactionItem>& data) {
|
||||
delete zsTrans;
|
||||
zsTrans = new QList<TransactionItem>();
|
||||
std::copy(data.begin(), data.end(), std::back_inserter(*zsTrans));
|
||||
|
||||
updateAllData();
|
||||
}
|
||||
|
||||
void TxTableModel::addNewData(const QList<TransactionItem>& data) {
|
||||
// Make sure we're expecting some data.
|
||||
Q_ASSERT(expectedData > 0);
|
||||
void TxTableModel::addZRecvData(const QList<TransactionItem>& data) {
|
||||
delete zrTrans;
|
||||
zrTrans = new QList<TransactionItem>();
|
||||
std::copy(data.begin(), data.end(), std::back_inserter(*zrTrans));
|
||||
|
||||
// Add all
|
||||
std::copy(data.begin(), data.end(), std::back_inserter(*newmodeldata));
|
||||
expectedData--;
|
||||
updateAllData();
|
||||
}
|
||||
|
||||
if (expectedData == 0) {
|
||||
delete modeldata;
|
||||
|
||||
modeldata = newmodeldata;
|
||||
newmodeldata = nullptr;
|
||||
void TxTableModel::addTData(const QList<TransactionItem>& data) {
|
||||
delete tTrans;
|
||||
tTrans = new QList<TransactionItem>();
|
||||
std::copy(data.begin(), data.end(), std::back_inserter(*tTrans));
|
||||
|
||||
// Sort by reverse time
|
||||
std::sort(modeldata->begin(), modeldata->end(), [=] (auto a, auto b) {
|
||||
return a.datetime > b.datetime; // reverse sort
|
||||
});
|
||||
updateAllData();
|
||||
}
|
||||
|
||||
dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1));
|
||||
layoutChanged();
|
||||
}
|
||||
void TxTableModel::updateAllData() {
|
||||
delete modeldata;
|
||||
modeldata = new QList<TransactionItem>();
|
||||
|
||||
if (tTrans != nullptr) std::copy( tTrans->begin(), tTrans->end(), std::back_inserter(*modeldata));
|
||||
if (zsTrans != nullptr) std::copy(zsTrans->begin(), zsTrans->end(), std::back_inserter(*modeldata));
|
||||
if (zrTrans != nullptr) std::copy(zrTrans->begin(), zrTrans->end(), std::back_inserter(*modeldata));
|
||||
|
||||
// Sort by reverse time
|
||||
std::sort(modeldata->begin(), modeldata->end(), [=] (auto a, auto b) {
|
||||
return a.datetime > b.datetime; // reverse sort
|
||||
});
|
||||
|
||||
dataChanged(index(0, 0), index(modeldata->size()-1, columnCount(index(0,0))-1));
|
||||
layoutChanged();
|
||||
}
|
||||
|
||||
int TxTableModel::rowCount(const QModelIndex&) const
|
||||
@@ -73,7 +87,13 @@ void TxTableModel::addNewData(const QList<TransactionItem>& data) {
|
||||
if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
|
||||
switch (index.column()) {
|
||||
case 0: return modeldata->at(index.row()).type;
|
||||
case 1: return modeldata->at(index.row()).address;
|
||||
case 1: {
|
||||
auto addr = modeldata->at(index.row()).address;
|
||||
if (addr.trimmed().isEmpty())
|
||||
return "(Shielded)";
|
||||
else
|
||||
return addr;
|
||||
}
|
||||
case 2: return QDateTime::fromSecsSinceEpoch(modeldata->at(index.row()).datetime).toLocalTime().toString();
|
||||
case 3: {
|
||||
if (role == Qt::DisplayRole)
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
#ifndef STRINGSTABLEMODEL_H
|
||||
#define STRINGSTABLEMODEL_H
|
||||
|
||||
#include "transactionitem.h"
|
||||
#include "precompiled.h"
|
||||
|
||||
struct TransactionItem;
|
||||
|
||||
class TxTableModel: public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
TxTableModel(QObject* parent);
|
||||
~TxTableModel();
|
||||
|
||||
void prepNewData (int expectedData);
|
||||
void addNewData (const QList<TransactionItem>& data);
|
||||
void addTData (const QList<TransactionItem>& data);
|
||||
void addZSentData(const QList<TransactionItem>& data);
|
||||
void addZRecvData(const QList<TransactionItem>& data);
|
||||
|
||||
QString getTxId(int row);
|
||||
|
||||
@@ -21,9 +23,13 @@ public:
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||
|
||||
private:
|
||||
QList<TransactionItem>* modeldata = nullptr;
|
||||
QList<TransactionItem>* newmodeldata = nullptr;
|
||||
int expectedData;
|
||||
void updateAllData();
|
||||
|
||||
QList<TransactionItem>* tTrans = nullptr;
|
||||
QList<TransactionItem>* zrTrans = nullptr; // Z received
|
||||
QList<TransactionItem>* zsTrans = nullptr; // Z sent
|
||||
|
||||
QList<TransactionItem>* modeldata = nullptr;
|
||||
|
||||
QList<QString> headers;
|
||||
};
|
||||
|
||||
@@ -18,8 +18,9 @@ public:
|
||||
static double getDevFee();
|
||||
static double getTotalFee();
|
||||
|
||||
static const int updateSpeed = 20 * 1000; // 20 sec
|
||||
static const int quickUpdateSpeed = 5 * 1000; // 5 sec
|
||||
static const int updateSpeed = 20 * 1000; // 20 sec
|
||||
static const int quickUpdateSpeed = 5 * 1000; // 5 sec
|
||||
static const int priceRefreshSpeed = 60 * 60 * 1000; // 1 hr
|
||||
private:
|
||||
Utils() = delete;
|
||||
};
|
||||
|
||||
@@ -48,6 +48,7 @@ SOURCES += \
|
||||
src/3rdparty/qrcode/QrSegment.cpp \
|
||||
src/settings.cpp \
|
||||
src/sendtab.cpp \
|
||||
src/senttxstore.cpp \
|
||||
src/txtablemodel.cpp \
|
||||
src/utils.cpp
|
||||
|
||||
@@ -63,7 +64,7 @@ HEADERS += \
|
||||
src/3rdparty/json/json.hpp \
|
||||
src/settings.h \
|
||||
src/txtablemodel.h \
|
||||
src/transactionitem.h \
|
||||
src/senttxstore.h \
|
||||
src/utils.h
|
||||
|
||||
FORMS += \
|
||||
|
||||
Reference in New Issue
Block a user