aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2016-10-10 15:29:56 +0200
committerRiccardo Spagni <ric@spagni.net>2016-10-10 15:29:56 +0200
commit053e7c7cdae92659500d01e619f039d4c15ea9d3 (patch)
treeca91d8dc8d661a8ef81ad6116e3f8574fb2b0d70 /src
parentfab66fec38a37c35bac90836a462404b5e4b464c (diff)
parentb1454c6a1366ccf044d9ff92ac1aa95d2249f06b (diff)
downloadmonzero-gui-053e7c7cdae92659500d01e619f039d4c15ea9d3.tar.gz
monzero-gui-053e7c7cdae92659500d01e619f039d4c15ea9d3.tar.xz
monzero-gui-053e7c7cdae92659500d01e619f039d4c15ea9d3.zip
Merge pull request #41
b1454c6 removed duplicated-by-mistake code (Ilya Kitaev) be135e3 Processing splash formatting improved (Ilya Kitaev) 54b22d2 Wizard: finish page: restore height hidden if not set (Ilya Kitaev) d67071a Settings page: layout items vertically (Ilya Kitaev) 4f4cc9c Settings page: small renaming and formatting (Ilya Kitaev) da552a0 History: Fixed filter collapse (Ilya Kitaev) 7ed623e Basic mode: updating balance properly (Ilya Kitaev) eafcf71 Minimized aka basic mode (Ilya Kitaev) a032c84 History: fee color (Ilya Kitaev) 60b2d90 History: display "fee" for sent transactions (Ilya Kitaev) 0ac27e1 History: filter by amount (Ilya Kitaev) 10c2786 added restore height to wizardFinish (Jacob Brydolf) 883762c removed daemon-blockchain-progress rests (Jacob Brydolf) 38d9034 Wizard: added restore-height (Jacob Brydolf) 7f0d6c4 persistentSettings: added restoreHeight (Jacob Brydolf) 9f336a5 libwalletqt: added restore-height parameter to recoveryWallet(); (Jacob Brydolf) 0e0b0be removed moneroComponets dependency (Jacob Brydolf) 38db5a4 Integrate splash counter with restore-height (Jacob Brydolf) 868610c fix conflict (Jacob Brydolf) 4901839 added comma/locale separation to sync counter (Jacob Brydolf) 8b748bf History: filter by amount (Ilya Kitaev) 9b09e83 History: filtering by paymentId and date (Ilya Kitaev) e3cea85 TransactionHistory: firstDateTime and lastDateTime properties (Ilya Kitaev) 9927182 Fix: unused dummy property removed (Ilya Kitaev) fd9ed56 Fix: DatePicker component doesn't update the date (Ilya Kitaev) 612c497 TransactionHistory sorting (Ilya Kitaev) e7e6c58 Adding sort-filter-proxy model (Ilya Kitaev) 0498c3b Transaction history is not crashing and refreshing properly (Ilya Kitaev) defc2a4 build: get_libwallet_api.sh accepts build type ("Debug" or "Release", release is default); extra build script that skips cmake invocations (Ilya Kitaev) 2966b0d settings page: daemon change + password prompt on seed view (Jacob Brydolf) 9700944 close wallet before reopen (Jacob Brydolf) 522b067 Simple transaction history (Ilya Kitaev) 9cc92bf added onPageCompleted (Jacob Brydolf) 53d3bc4 Receive page: display long address from the beginning in TextField (Ilya Kitaev) 5814c19 Fixed overlapping clipboard icons on recieve page (Jacob Brydolf) cd1247c Added option to show seed on settings-page (Jacob Brydolf) af70c81 Added daemon BC sync progress bar depends on https://github.com/monero-project/monero/pull/1173 (Jacob Brydolf) 6a50951 Removed hardcoded height from PasswordDialog (Ilya Kitaev) 819be91 Integrating TransactionHistoryModel (Ilya Kitaev) 56c3579 Replaced Loader with StackView; Fixed payment id generation (Ilya Kitaev) 0ff3fd3 added TransactionHistoryModel; renamings (Ilya Kitaev) bd8646d UX improvements in wizard - password page styling - tab navigation / focus on load - click language loads next page (Jacob Brydolf)
Diffstat (limited to 'src')
-rw-r--r--src/libwalletqt/TransactionHistory.cpp76
-rw-r--r--src/libwalletqt/TransactionHistory.h16
-rw-r--r--src/libwalletqt/TransactionInfo.cpp32
-rw-r--r--src/libwalletqt/TransactionInfo.h34
-rw-r--r--src/libwalletqt/Wallet.cpp42
-rw-r--r--src/libwalletqt/Wallet.h31
-rw-r--r--src/libwalletqt/WalletManager.cpp4
-rw-r--r--src/libwalletqt/WalletManager.h2
-rw-r--r--src/model/TransactionHistoryModel.cpp126
-rw-r--r--src/model/TransactionHistoryModel.h68
-rw-r--r--src/model/TransactionHistorySortFilterModel.cpp209
-rw-r--r--src/model/TransactionHistorySortFilterModel.h79
12 files changed, 667 insertions, 52 deletions
diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp
index 2c2c9f73..7fd7c7bc 100644
--- a/src/libwalletqt/TransactionHistory.cpp
+++ b/src/libwalletqt/TransactionHistory.cpp
@@ -2,49 +2,89 @@
#include "TransactionInfo.h"
#include <wallet/wallet2_api.h>
+#include <QDebug>
-int TransactionHistory::count() const
-{
- return m_pimpl->count();
-}
TransactionInfo *TransactionHistory::transaction(int index)
{
- // box up Bitmonero::TransactionInfo
- Bitmonero::TransactionInfo * impl = m_pimpl->transaction(index);
- TransactionInfo * result = new TransactionInfo(impl, this);
- return result;
-}
-TransactionInfo *TransactionHistory::transaction(const QString &id)
-{
- // box up Bitmonero::TransactionInfo
- Bitmonero::TransactionInfo * impl = m_pimpl->transaction(id.toStdString());
- TransactionInfo * result = new TransactionInfo(impl, this);
- return result;
+ if (index < 0 || index >= m_tinfo.size()) {
+ qCritical("%s: no transaction info for index %d", __FUNCTION__, index);
+ qCritical("%s: there's %d transactions in backend", __FUNCTION__, m_pimpl->count());
+ return nullptr;
+ }
+ return m_tinfo.at(index);
}
+//// XXX: not sure if this method really needed;
+//TransactionInfo *TransactionHistory::transaction(const QString &id)
+//{
+// return nullptr;
+//}
+
QList<TransactionInfo *> TransactionHistory::getAll() const
{
+ // XXX this invalidates previously saved history that might be used by model
+ emit refreshStarted();
qDeleteAll(m_tinfo);
m_tinfo.clear();
+
+ QDateTime firstDateTime = QDateTime::currentDateTime();
+ QDateTime lastDateTime = QDateTime(QDate(1970, 1, 1));
+
TransactionHistory * parent = const_cast<TransactionHistory*>(this);
for (const auto i : m_pimpl->getAll()) {
TransactionInfo * ti = new TransactionInfo(i, parent);
m_tinfo.append(ti);
+ // looking for transactions timestamp scope
+ if (ti->timestamp() >= lastDateTime) {
+ lastDateTime = ti->timestamp();
+ }
+ if (ti->timestamp() <= firstDateTime) {
+ firstDateTime = ti->timestamp();
+ }
+ }
+ emit refreshFinished();
+
+ if (m_firstDateTime != firstDateTime) {
+ m_firstDateTime = firstDateTime;
+ emit firstDateTimeChanged();
+ }
+ if (m_lastDateTime != lastDateTime) {
+ m_lastDateTime = lastDateTime;
+ emit lastDateTimeChanged();
}
+
return m_tinfo;
}
void TransactionHistory::refresh()
{
- // XXX this invalidates previously saved history that might be used by clients
+ // rebuilding transaction list in wallet_api;
m_pimpl->refresh();
- emit invalidated();
+ // copying list here and keep track on every item to avoid memleaks
+ getAll();
}
+quint64 TransactionHistory::count() const
+{
+ return m_tinfo.count();
+}
+
+QDateTime TransactionHistory::firstDateTime() const
+{
+ return m_firstDateTime;
+}
+
+QDateTime TransactionHistory::lastDateTime() const
+{
+ return m_lastDateTime;
+}
+
+
TransactionHistory::TransactionHistory(Bitmonero::TransactionHistory *pimpl, QObject *parent)
: QObject(parent), m_pimpl(pimpl)
{
-
+ m_firstDateTime = QDateTime(QDate(1970, 1, 1));
+ m_lastDateTime = QDateTime::currentDateTime();
}
diff --git a/src/libwalletqt/TransactionHistory.h b/src/libwalletqt/TransactionHistory.h
index a6287506..ee05787a 100644
--- a/src/libwalletqt/TransactionHistory.h
+++ b/src/libwalletqt/TransactionHistory.h
@@ -3,6 +3,7 @@
#include <QObject>
#include <QList>
+#include <QDateTime>
namespace Bitmonero {
class TransactionHistory;
@@ -14,16 +15,23 @@ class TransactionHistory : public QObject
{
Q_OBJECT
Q_PROPERTY(int count READ count)
+ Q_PROPERTY(QDateTime firstDateTime READ firstDateTime NOTIFY firstDateTimeChanged)
+ Q_PROPERTY(QDateTime lastDateTime READ lastDateTime NOTIFY lastDateTimeChanged)
public:
- int count() const;
Q_INVOKABLE TransactionInfo *transaction(int index);
- Q_INVOKABLE TransactionInfo * transaction(const QString &id);
+ // Q_INVOKABLE TransactionInfo * transaction(const QString &id);
Q_INVOKABLE QList<TransactionInfo*> getAll() const;
Q_INVOKABLE void refresh();
+ quint64 count() const;
+ QDateTime firstDateTime() const;
+ QDateTime lastDateTime() const;
signals:
- void invalidated();
+ void refreshStarted() const;
+ void refreshFinished() const;
+ void firstDateTimeChanged() const;
+ void lastDateTimeChanged() const;
public slots:
@@ -36,6 +44,8 @@ private:
Bitmonero::TransactionHistory * m_pimpl;
mutable QList<TransactionInfo*> m_tinfo;
+ mutable QDateTime m_firstDateTime;
+ mutable QDateTime m_lastDateTime;
};
diff --git a/src/libwalletqt/TransactionInfo.cpp b/src/libwalletqt/TransactionInfo.cpp
index 192e85e5..248fd73e 100644
--- a/src/libwalletqt/TransactionInfo.cpp
+++ b/src/libwalletqt/TransactionInfo.cpp
@@ -1,4 +1,6 @@
#include "TransactionInfo.h"
+#include "WalletManager.h"
+
#include <QDateTime>
TransactionInfo::Direction TransactionInfo::direction() const
@@ -16,15 +18,21 @@ bool TransactionInfo::isFailed() const
return m_pimpl->isFailed();
}
-quint64 TransactionInfo::amount() const
+
+double TransactionInfo::amount() const
{
- return m_pimpl->amount();
+ // there's no unsigned uint64 for JS, so better use double
+ return WalletManager::instance()->displayAmount(m_pimpl->amount()).toDouble();
}
-quint64 TransactionInfo::fee() const
+QString TransactionInfo::displayAmount() const
{
- return m_pimpl->fee();
+ return WalletManager::instance()->displayAmount(m_pimpl->amount());
+}
+QString TransactionInfo::fee() const
+{
+ return WalletManager::instance()->displayAmount(m_pimpl->fee());
}
quint64 TransactionInfo::blockHeight() const
@@ -37,13 +45,23 @@ QString TransactionInfo::hash() const
return QString::fromStdString(m_pimpl->hash());
}
-QString TransactionInfo::timestamp()
+QDateTime TransactionInfo::timestamp() const
{
- QString result = QDateTime::fromTime_t(m_pimpl->timestamp()).toString(Qt::ISODate);
+ QDateTime result = QDateTime::fromTime_t(m_pimpl->timestamp());
return result;
}
-QString TransactionInfo::paymentId()
+QString TransactionInfo::date() const
+{
+ return timestamp().date().toString(Qt::ISODate);
+}
+
+QString TransactionInfo::time() const
+{
+ return timestamp().time().toString(Qt::ISODate);
+}
+
+QString TransactionInfo::paymentId() const
{
return QString::fromStdString(m_pimpl->paymentId());
}
diff --git a/src/libwalletqt/TransactionInfo.h b/src/libwalletqt/TransactionInfo.h
index 62c26e2f..f30f42fa 100644
--- a/src/libwalletqt/TransactionInfo.h
+++ b/src/libwalletqt/TransactionInfo.h
@@ -1,8 +1,9 @@
#ifndef TRANSACTIONINFO_H
#define TRANSACTIONINFO_H
-#include <QObject>
#include <wallet/wallet2_api.h>
+#include <QObject>
+#include <QDateTime>
class TransactionInfo : public QObject
{
@@ -10,19 +11,25 @@ class TransactionInfo : public QObject
Q_PROPERTY(Direction direction READ direction)
Q_PROPERTY(bool isPending READ isPending)
Q_PROPERTY(bool isFailed READ isFailed)
- Q_PROPERTY(quint64 amount READ amount)
- Q_PROPERTY(quint64 fee READ fee)
+ Q_PROPERTY(double amount READ amount)
+ Q_PROPERTY(QString displayAmount READ displayAmount)
+ Q_PROPERTY(QString fee READ fee)
Q_PROPERTY(quint64 blockHeight READ blockHeight)
Q_PROPERTY(QString hash READ hash)
- Q_PROPERTY(QString timestamp READ timestamp)
+ Q_PROPERTY(QDateTime timestamp READ timestamp)
+ Q_PROPERTY(QString date READ date)
+ Q_PROPERTY(QString time READ time)
Q_PROPERTY(QString paymentId READ paymentId)
public:
enum Direction {
Direction_In = Bitmonero::TransactionInfo::Direction_In,
- Direction_Out = Bitmonero::TransactionInfo::Direction_Out
+ Direction_Out = Bitmonero::TransactionInfo::Direction_Out,
+ Direction_Both // invalid direction value, used for filtering
};
+ Q_ENUM(Direction)
+
// TODO: implement as separate class;
// struct Transfer {
@@ -30,16 +37,21 @@ public:
// const uint64_t amount;
// const std::string address;
// };
+
Direction direction() const;
bool isPending() const;
bool isFailed() const;
- quint64 amount() const;
- quint64 fee() const;
+ double amount() const;
+ QString displayAmount() const;
+ QString fee() const;
quint64 blockHeight() const;
//! transaction_id
QString hash() const;
- QString timestamp();
- QString paymentId();
+ QDateTime timestamp() const;
+ QString date() const;
+ QString time() const;
+ QString paymentId() const;
+
// TODO: implement it
//! only applicable for output transactions
@@ -51,4 +63,8 @@ private:
Bitmonero::TransactionInfo * m_pimpl;
};
+// in order to wrap it to QVariant
+Q_DECLARE_METATYPE(TransactionInfo*)
+
+
#endif // TRANSACTIONINFO_H
diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp
index 987f4640..b52963bc 100644
--- a/src/libwalletqt/Wallet.cpp
+++ b/src/libwalletqt/Wallet.cpp
@@ -1,6 +1,8 @@
#include "Wallet.h"
#include "PendingTransaction.h"
#include "TransactionHistory.h"
+#include "model/TransactionHistoryModel.h"
+#include "model/TransactionHistorySortFilterModel.h"
#include "wallet/wallet2_api.h"
#include <QFile>
@@ -31,7 +33,6 @@ public:
virtual void moneyReceived(const std::string &txId, uint64_t amount)
{
-
qDebug() << __FUNCTION__;
emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
}
@@ -59,7 +60,10 @@ private:
Wallet * m_wallet;
};
-
+Wallet::Wallet(QObject * parent)
+ : Wallet(nullptr, parent)
+{
+}
QString Wallet::getSeed() const
{
@@ -86,6 +90,11 @@ bool Wallet::connected() const
return m_walletImpl->connected();
}
+bool Wallet::synchronized() const
+{
+ return m_walletImpl->synchronized();
+}
+
QString Wallet::errorString() const
{
return QString::fromStdString(m_walletImpl->errorString());
@@ -153,9 +162,16 @@ quint64 Wallet::daemonBlockChainHeight() const
return m_daemonBlockChainHeight;
}
+quint64 Wallet::daemonBlockChainTargetHeight() const
+{
+ m_daemonBlockChainTargetHeight = m_walletImpl->daemonBlockChainTargetHeight();
+ return m_daemonBlockChainTargetHeight;
+}
+
bool Wallet::refresh()
{
bool result = m_walletImpl->refresh();
+ m_history->refresh();
if (result)
emit updated();
return result;
@@ -193,15 +209,24 @@ void Wallet::disposeTransaction(PendingTransaction *t)
delete t;
}
-TransactionHistory *Wallet::history()
+TransactionHistory *Wallet::history() const
{
- if (!m_history) {
- Bitmonero::TransactionHistory * impl = m_walletImpl->history();
- m_history = new TransactionHistory(impl, this);
- }
return m_history;
}
+TransactionHistorySortFilterModel *Wallet::historyModel() const
+{
+ if (!m_historyModel) {
+ Wallet * w = const_cast<Wallet*>(this);
+ m_historyModel = new TransactionHistoryModel(w);
+ m_historyModel->setTransactionHistory(this->history());
+ m_historySortFilterModel = new TransactionHistorySortFilterModel(w);
+ m_historySortFilterModel->setSourceModel(m_historyModel);
+ }
+
+ return m_historySortFilterModel;
+}
+
QString Wallet::generatePaymentId() const
{
@@ -228,13 +253,16 @@ Wallet::Wallet(Bitmonero::Wallet *w, QObject *parent)
: QObject(parent)
, m_walletImpl(w)
, m_history(nullptr)
+ , m_historyModel(nullptr)
, m_daemonBlockChainHeight(0)
, m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS)
{
+ m_history = new TransactionHistory(m_walletImpl->history(), this);
m_walletImpl->setListener(new WalletListenerImpl(this));
}
Wallet::~Wallet()
{
+
Bitmonero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
}
diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h
index 703bd186..d794550a 100644
--- a/src/libwalletqt/Wallet.h
+++ b/src/libwalletqt/Wallet.h
@@ -13,6 +13,8 @@ namespace Bitmonero {
class TransactionHistory;
+class TransactionHistoryModel;
+class TransactionHistorySortFilterModel;
class Wallet : public QObject
{
@@ -21,14 +23,18 @@ class Wallet : public QObject
Q_PROPERTY(QString seedLanguage READ getSeedLanguage)
Q_PROPERTY(Status status READ status)
Q_PROPERTY(bool connected READ connected)
+ Q_PROPERTY(bool synchronized READ synchronized)
Q_PROPERTY(QString errorString READ errorString)
Q_PROPERTY(QString address READ address)
Q_PROPERTY(quint64 balance READ balance)
Q_PROPERTY(quint64 unlockedBalance READ unlockedBalance)
Q_PROPERTY(TransactionHistory * history READ history)
Q_PROPERTY(QString paymentId READ paymentId WRITE setPaymentId)
+ Q_PROPERTY(TransactionHistorySortFilterModel * historyModel READ historyModel NOTIFY historyModelChanged)
public:
+
+
enum Status {
Status_Ok = Bitmonero::Wallet::Status_Ok,
Status_Error = Bitmonero::Wallet::Status_Error
@@ -48,9 +54,13 @@ public:
//! returns last operation's status
Status status() const;
- //! returns of wallet connected
+ //! returns true if wallet connected
bool connected() const;
+ //! returns true if wallet was ever synchronized
+ bool synchronized() const;
+
+
//! returns last operation's error message
QString errorString() const;
@@ -88,10 +98,12 @@ public:
//! returns daemon's blockchain height
Q_INVOKABLE quint64 daemonBlockChainHeight() const;
+ //! returns daemon's blockchain target height
+ Q_INVOKABLE quint64 daemonBlockChainTargetHeight() const;
+
//! refreshes the wallet
Q_INVOKABLE bool refresh();
-
//! refreshes the wallet asynchronously
Q_INVOKABLE void refreshAsync();
@@ -109,7 +121,10 @@ public:
Q_INVOKABLE void disposeTransaction(PendingTransaction * t);
//! returns transaction history
- TransactionHistory * history();
+ TransactionHistory * history() const;
+
+ //! returns transaction history model
+ TransactionHistorySortFilterModel *historyModel() const;
//! generate payment id
Q_INVOKABLE QString generatePaymentId() const;
@@ -136,12 +151,13 @@ signals:
void moneySpent(const QString &txId, quint64 amount);
void moneyReceived(const QString &txId, quint64 amount);
void newBlock(quint64 height);
+ void historyModelChanged() const;
private:
+ Wallet(QObject * parent = nullptr);
Wallet(Bitmonero::Wallet *w, QObject * parent = 0);
~Wallet();
-
private:
friend class WalletManager;
friend class WalletListenerImpl;
@@ -149,11 +165,16 @@ private:
Bitmonero::Wallet * m_walletImpl;
// history lifetime managed by wallet;
TransactionHistory * m_history;
+ // Used for UI history view
+ mutable TransactionHistoryModel * m_historyModel;
+ mutable TransactionHistorySortFilterModel * m_historySortFilterModel;
QString m_paymentId;
mutable QTime m_daemonBlockChainHeightTime;
mutable quint64 m_daemonBlockChainHeight;
int m_daemonBlockChainHeightTtl;
-
+ mutable quint64 m_daemonBlockChainTargetHeight;
};
+
+
#endif // WALLET_H
diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp
index ea299cdc..2e1fe57b 100644
--- a/src/libwalletqt/WalletManager.cpp
+++ b/src/libwalletqt/WalletManager.cpp
@@ -61,9 +61,9 @@ void WalletManager::openWalletAsync(const QString &path, const QString &password
}
-Wallet *WalletManager::recoveryWallet(const QString &path, const QString &memo, bool testnet)
+Wallet *WalletManager::recoveryWallet(const QString &path, const QString &memo, bool testnet, quint64 restoreHeight)
{
- Bitmonero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), memo.toStdString(), testnet);
+ Bitmonero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), memo.toStdString(), testnet, restoreHeight);
Wallet * wallet = new Wallet(w);
return wallet;
}
diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h
index 23c56925..24fb60fc 100644
--- a/src/libwalletqt/WalletManager.h
+++ b/src/libwalletqt/WalletManager.h
@@ -48,7 +48,7 @@ public:
// wizard: recoveryWallet path; hint: internally it recorvers wallet and set password = ""
Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &memo,
- bool testnet = false);
+ bool testnet = false, quint64 restoreHeight = 0);
/*!
* \brief closeWallet - closes wallet and frees memory
diff --git a/src/model/TransactionHistoryModel.cpp b/src/model/TransactionHistoryModel.cpp
new file mode 100644
index 00000000..1ad17944
--- /dev/null
+++ b/src/model/TransactionHistoryModel.cpp
@@ -0,0 +1,126 @@
+#include "TransactionHistoryModel.h"
+#include "TransactionHistory.h"
+#include "TransactionInfo.h"
+
+#include <QDateTime>
+
+
+TransactionHistoryModel::TransactionHistoryModel(QObject *parent)
+ : QAbstractListModel(parent), m_transactionHistory(nullptr)
+{
+
+}
+
+void TransactionHistoryModel::setTransactionHistory(TransactionHistory *th)
+{
+ beginResetModel();
+ m_transactionHistory = th;
+ endResetModel();
+
+ connect(m_transactionHistory, &TransactionHistory::refreshStarted,
+ this, &TransactionHistoryModel::beginResetModel);
+ connect(m_transactionHistory, &TransactionHistory::refreshFinished,
+ this, &TransactionHistoryModel::endResetModel);
+
+ emit transactionHistoryChanged();
+}
+
+TransactionHistory *TransactionHistoryModel::transactionHistory() const
+{
+ return m_transactionHistory;
+}
+
+QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
+{
+ if (!m_transactionHistory) {
+ return QVariant();
+ }
+
+ if (index.row() < 0 || (unsigned)index.row() >= m_transactionHistory->count()) {
+ return QVariant();
+ }
+
+ TransactionInfo * tInfo = m_transactionHistory->transaction(index.row());
+
+
+ Q_ASSERT(tInfo);
+ if (!tInfo) {
+ qCritical("%s: internal error: no transaction info for index %d", __FUNCTION__, index.row());
+ return QVariant();
+ }
+ QVariant result;
+ switch (role) {
+ case TransactionRole:
+ result = QVariant::fromValue(tInfo);
+ break;
+ case TransactionDirectionRole:
+ result = QVariant::fromValue(tInfo->direction());
+ break;
+ case TransactionPendingRole:
+ result = tInfo->isPending();
+ break;
+ case TransactionFailedRole:
+ result = tInfo->isFailed();
+ break;
+ case TransactionAmountRole:
+ result = tInfo->amount();
+ break;
+ case TransactionDisplayAmountRole:
+ result = tInfo->displayAmount();
+ break;
+ case TransactionFeeRole:
+ result = tInfo->fee();
+ break;
+ case TransactionBlockHeightRole:
+ result = tInfo->blockHeight();
+ break;
+ case TransactionHashRole:
+ result = tInfo->hash();
+ break;
+ case TransactionTimeStampRole:
+ result = tInfo->timestamp();
+ break;
+ case TransactionPaymentIdRole:
+ result = tInfo->paymentId();
+ break;
+ case TransactionIsOutRole:
+ result = tInfo->direction() == TransactionInfo::Direction_Out;
+ break;
+ case TransactionDateRole:
+ result = tInfo->date();
+ break;
+ case TransactionTimeRole:
+ result = tInfo->time();
+ break;
+ }
+
+ return result;
+}
+
+int TransactionHistoryModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_transactionHistory ? m_transactionHistory->count() : 0;
+}
+
+QHash<int, QByteArray> TransactionHistoryModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames = QAbstractListModel::roleNames();
+ roleNames.insert(TransactionRole, "transaction");
+ roleNames.insert(TransactionDirectionRole, "direction");
+ roleNames.insert(TransactionPendingRole, "isPending");
+ roleNames.insert(TransactionFailedRole, "isFailed");
+ roleNames.insert(TransactionAmountRole, "amount");
+ roleNames.insert(TransactionDisplayAmountRole, "displayAmount");
+ roleNames.insert(TransactionFeeRole, "fee");
+ roleNames.insert(TransactionBlockHeightRole, "blockHeight");
+ roleNames.insert(TransactionHashRole, "hash");
+ roleNames.insert(TransactionTimeStampRole, "timeStamp");
+ roleNames.insert(TransactionPaymentIdRole, "paymentId");
+ roleNames.insert(TransactionIsOutRole, "isOut");
+ roleNames.insert(TransactionDateRole, "date");
+ roleNames.insert(TransactionTimeRole, "time");
+ return roleNames;
+}
+
+
diff --git a/src/model/TransactionHistoryModel.h b/src/model/TransactionHistoryModel.h
new file mode 100644
index 00000000..0960ceae
--- /dev/null
+++ b/src/model/TransactionHistoryModel.h
@@ -0,0 +1,68 @@
+#ifndef TRANSACTIONHISTORYMODEL_H
+#define TRANSACTIONHISTORYMODEL_H
+
+#include <QAbstractListModel>
+
+class TransactionHistory;
+class TransactionInfo;
+
+/**
+ * @brief The TransactionHistoryModel class - read-only list model for Transaction History
+ */
+
+class TransactionHistoryModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(TransactionHistory * transactionHistory READ transactionHistory WRITE setTransactionHistory NOTIFY transactionHistoryChanged)
+
+public:
+ enum TransactionInfoRole {
+ TransactionRole = Qt::UserRole + 1, // for the TransactionInfo object;
+ TransactionDirectionRole,
+ TransactionPendingRole,
+ TransactionFailedRole,
+ TransactionAmountRole,
+ TransactionDisplayAmountRole,
+ TransactionFeeRole,
+ TransactionBlockHeightRole,
+ TransactionHashRole,
+ TransactionTimeStampRole,
+ TransactionPaymentIdRole,
+ // extra role (alias) for TransactionDirectionRole (as UI currently wants just boolean "out")
+ TransactionIsOutRole,
+ // extra roles for date and time (as UI wants date and time separately)
+ TransactionDateRole,
+ TransactionTimeRole
+ };
+ Q_ENUM(TransactionInfoRole)
+
+ TransactionHistoryModel(QObject * parent = 0);
+ void setTransactionHistory(TransactionHistory * th);
+ TransactionHistory * transactionHistory() const;
+ /**
+ * @brief dateFrom - returns firstmost transaction datetime
+ * @return
+ */
+ QDateTime firstDateTime() const;
+
+ /**
+ * @brief dateTo - returns lastmost transaction datetime
+ * @return
+ */
+ QDateTime lastDateTime() const;
+
+
+
+ /// QAbstractListModel
+ virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
+ virtual int rowCount(const QModelIndex & parent = QModelIndex()) const override;
+ virtual QHash<int, QByteArray> roleNames() const override;
+
+signals:
+ void transactionHistoryChanged();
+
+private:
+ TransactionHistory * m_transactionHistory;
+};
+
+#endif // TRANSACTIONHISTORYMODEL_H
diff --git a/src/model/TransactionHistorySortFilterModel.cpp b/src/model/TransactionHistorySortFilterModel.cpp
new file mode 100644
index 00000000..6d6a2b44
--- /dev/null
+++ b/src/model/TransactionHistorySortFilterModel.cpp
@@ -0,0 +1,209 @@
+#include "TransactionHistorySortFilterModel.h"
+#include "TransactionHistoryModel.h"
+
+#include <QDebug>
+
+namespace {
+ /**
+ * helper to extract scope value from filter
+ */
+ template <typename T>
+ T scopeFilterValue(const QMap<int, QVariant> &filters, int role, int scopeIndex)
+ {
+ if (!filters.contains(role)) {
+ return T();
+ }
+ return filters.value(role).toList().at(scopeIndex).value<T>();
+ }
+
+ /**
+ * helper to setup scope value to filter
+ */
+ template <typename T>
+ void setScopeFilterValue(QMap<int, QVariant> &filters, int role, int scopeIndex, const T &value)
+ {
+ QVariantList scopeFilter;
+
+ if (filters.contains(role)) {
+ scopeFilter = filters.value(role).toList();
+ }
+ while (scopeFilter.size() < 2) {
+ scopeFilter.append(T());
+ }
+ scopeFilter[scopeIndex] = QVariant::fromValue(value);
+ filters[role] = scopeFilter;
+ }
+}
+
+
+TransactionHistorySortFilterModel::TransactionHistorySortFilterModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+ setDynamicSortFilter(true);
+}
+
+QString TransactionHistorySortFilterModel::paymentIdFilter() const
+{
+ return m_filterValues.value(TransactionHistoryModel::TransactionPaymentIdRole).toString();
+}
+
+void TransactionHistorySortFilterModel::setPaymentIdFilter(const QString &arg)
+{
+ if (paymentIdFilter() != arg) {
+ m_filterValues[TransactionHistoryModel::TransactionPaymentIdRole] = arg;
+ emit paymentIdFilterChanged();
+ invalidateFilter();
+ }
+}
+
+QDate TransactionHistorySortFilterModel::dateFromFilter() const
+{
+ return scopeFilterValue<QDate>(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::From);
+}
+
+void TransactionHistorySortFilterModel::setDateFromFilter(const QDate &date)
+{
+ if (date != dateFromFilter()) {
+ setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::From, date);
+ emit dateFromFilterChanged();
+ invalidateFilter();
+ }
+}
+
+QDate TransactionHistorySortFilterModel::dateToFilter() const
+{
+ return scopeFilterValue<QDate>(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::To);
+}
+
+void TransactionHistorySortFilterModel::setDateToFilter(const QDate &date)
+{
+ if (date != dateToFilter()) {
+ setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionTimeStampRole, ScopeIndex::To, date);
+ emit dateToFilterChanged();
+ invalidateFilter();
+ }
+}
+
+double TransactionHistorySortFilterModel::amountFromFilter() const
+{
+ return scopeFilterValue<double>(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::From);
+}
+
+void TransactionHistorySortFilterModel::setAmountFromFilter(double value)
+{
+ if (value != amountFromFilter()) {
+ setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::From, value);
+ emit amountFromFilterChanged();
+ invalidateFilter();
+ }
+}
+
+double TransactionHistorySortFilterModel::amountToFilter() const
+{
+ return scopeFilterValue<double>(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::To);
+}
+
+void TransactionHistorySortFilterModel::setAmountToFilter(double value)
+{
+ if (value != amountToFilter()) {
+ setScopeFilterValue(m_filterValues, TransactionHistoryModel::TransactionAmountRole, ScopeIndex::To, value);
+ emit amountToFilterChanged();
+ invalidateFilter();
+ }
+}
+
+int TransactionHistorySortFilterModel::directionFilter() const
+{
+ return m_filterValues.value(TransactionHistoryModel::TransactionDirectionRole).value<TransactionInfo::Direction>();
+}
+
+void TransactionHistorySortFilterModel::setDirectionFilter(int value)
+{
+ if (value != directionFilter()) {
+ m_filterValues[TransactionHistoryModel::TransactionDirectionRole] = QVariant::fromValue(value);
+ emit directionFilterChanged();
+ invalidateFilter();
+ }
+}
+
+
+void TransactionHistorySortFilterModel::sort(int column, Qt::SortOrder order)
+{
+ QSortFilterProxyModel::sort(column, order);
+}
+
+TransactionHistory *TransactionHistorySortFilterModel::transactionHistory() const
+{
+ const TransactionHistoryModel * model = static_cast<const TransactionHistoryModel*> (sourceModel());
+ return model->transactionHistory();
+}
+
+bool TransactionHistorySortFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+
+ if (source_row < 0 || source_row >= sourceModel()->rowCount()) {
+ return false;
+ }
+
+ QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
+ if (!index.isValid()) {
+ return false;
+ }
+
+ bool result = true;
+
+ // iterating through filters
+ for (int role : m_filterValues.keys()) {
+ if (m_filterValues.contains(role)) {
+ QVariant data = sourceModel()->data(index, role);
+ switch (role) {
+ case TransactionHistoryModel::TransactionPaymentIdRole:
+ result = data.toString().contains(paymentIdFilter());
+ break;
+ case TransactionHistoryModel::TransactionTimeStampRole:
+ {
+ QDateTime from = QDateTime(dateFromFilter());
+ QDateTime to = QDateTime(dateToFilter());
+ to = to.addDays(1); // including upperbound
+ QDateTime timestamp = data.toDateTime();
+ bool matchFrom = from.isNull() || timestamp.isNull() || timestamp >= from;
+ bool matchTo = to.isNull() || timestamp.isNull() || timestamp <= to;
+ result = matchFrom && matchTo;
+ }
+ break;
+ case TransactionHistoryModel::TransactionAmountRole:
+ {
+ double from = amountFromFilter();
+ double to = amountToFilter();
+ double amount = data.toDouble();
+
+ bool matchFrom = from <= 0 || amount >= from;
+ bool matchTo = to <= 0 || amount <= to;
+ result = matchFrom && matchTo;
+ }
+ break;
+ case TransactionHistoryModel::TransactionDirectionRole:
+ result = directionFilter() == TransactionInfo::Direction_Both ? true
+ : data.toInt() == directionFilter();
+
+
+ break;
+
+ default:
+ break;
+ }
+
+
+ if (!result) { // stop the loop once filter doesn't match
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+bool TransactionHistorySortFilterModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ return QSortFilterProxyModel::lessThan(source_left, source_right);
+}
diff --git a/src/model/TransactionHistorySortFilterModel.h b/src/model/TransactionHistorySortFilterModel.h
new file mode 100644
index 00000000..1b5c2fce
--- /dev/null
+++ b/src/model/TransactionHistorySortFilterModel.h
@@ -0,0 +1,79 @@
+#ifndef TRANSACTIONHISTORYSORTFILTERMODEL_H
+#define TRANSACTIONHISTORYSORTFILTERMODEL_H
+
+#include "TransactionInfo.h"
+
+#include <QSortFilterProxyModel>
+#include <QMap>
+#include <QVariant>
+#include <QDate>
+
+
+class TransactionHistory;
+
+class TransactionHistorySortFilterModel: public QSortFilterProxyModel
+{
+ Q_OBJECT
+ Q_PROPERTY(QString paymentIdFilter READ paymentIdFilter WRITE setPaymentIdFilter NOTIFY paymentIdFilterChanged)
+ Q_PROPERTY(QDate dateFromFilter READ dateFromFilter WRITE setDateFromFilter NOTIFY dateFromFilterChanged)
+ Q_PROPERTY(QDate dateToFilter READ dateToFilter WRITE setDateToFilter NOTIFY dateToFilterChanged)
+ Q_PROPERTY(double amountFromFilter READ amountFromFilter WRITE setAmountFromFilter NOTIFY amountFromFilterChanged)
+ Q_PROPERTY(double amountToFilter READ amountToFilter WRITE setAmountToFilter NOTIFY amountToFilterChanged)
+ Q_PROPERTY(int directionFilter READ directionFilter WRITE setDirectionFilter NOTIFY directionFilterChanged)
+
+ Q_PROPERTY(TransactionHistory * transactionHistory READ transactionHistory)
+
+public:
+ TransactionHistorySortFilterModel(QObject * parent = nullptr);
+ //! filtering by payment id
+ QString paymentIdFilter() const;
+ void setPaymentIdFilter(const QString &arg);
+
+ //! filtering by date (lower bound)
+ QDate dateFromFilter() const;
+ void setDateFromFilter(const QDate &date);
+
+ //! filtering by to date (upper bound)
+ QDate dateToFilter() const;
+ void setDateToFilter(const QDate &date);
+
+ //! filtering by amount (lower bound)
+ double amountFromFilter() const;
+ void setAmountFromFilter(double value);
+
+ //! filtering by amount (upper bound)
+ double amountToFilter() const;
+ void setAmountToFilter(double value);
+
+ //! filtering by direction
+ int directionFilter() const;
+ void setDirectionFilter(int value);
+
+ Q_INVOKABLE void sort(int column, Qt::SortOrder order);
+ TransactionHistory * transactionHistory() const;
+
+signals:
+ void paymentIdFilterChanged();
+ void dateFromFilterChanged();
+ void dateToFilterChanged();
+ void amountFromFilterChanged();
+ void amountToFilterChanged();
+ void directionFilterChanged();
+
+protected:
+ // QSortFilterProxyModel overrides
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+ virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
+
+
+private:
+ enum ScopeIndex {
+ From = 0,
+ To = 1
+ };
+
+private:
+ QMap<int, QVariant> m_filterValues;
+};
+
+#endif // TRANSACTIONHISTORYSORTFILTERMODEL_H