diff options
| author | Riccardo Spagni <ric@spagni.net> | 2016-10-10 15:29:56 +0200 |
|---|---|---|
| committer | Riccardo Spagni <ric@spagni.net> | 2016-10-10 15:29:56 +0200 |
| commit | 053e7c7cdae92659500d01e619f039d4c15ea9d3 (patch) | |
| tree | ca91d8dc8d661a8ef81ad6116e3f8574fb2b0d70 /src | |
| parent | fab66fec38a37c35bac90836a462404b5e4b464c (diff) | |
| parent | b1454c6a1366ccf044d9ff92ac1aa95d2249f06b (diff) | |
| download | monzero-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.cpp | 76 | ||||
| -rw-r--r-- | src/libwalletqt/TransactionHistory.h | 16 | ||||
| -rw-r--r-- | src/libwalletqt/TransactionInfo.cpp | 32 | ||||
| -rw-r--r-- | src/libwalletqt/TransactionInfo.h | 34 | ||||
| -rw-r--r-- | src/libwalletqt/Wallet.cpp | 42 | ||||
| -rw-r--r-- | src/libwalletqt/Wallet.h | 31 | ||||
| -rw-r--r-- | src/libwalletqt/WalletManager.cpp | 4 | ||||
| -rw-r--r-- | src/libwalletqt/WalletManager.h | 2 | ||||
| -rw-r--r-- | src/model/TransactionHistoryModel.cpp | 126 | ||||
| -rw-r--r-- | src/model/TransactionHistoryModel.h | 68 | ||||
| -rw-r--r-- | src/model/TransactionHistorySortFilterModel.cpp | 209 | ||||
| -rw-r--r-- | src/model/TransactionHistorySortFilterModel.h | 79 |
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 |
