diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index e9ea81d..cac888d 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -3,6 +3,10 @@ #include "Wallet.h" +#include +#include +#include + #include "PendingTransaction.h" #include "UnsignedTransaction.h" #include "TransactionHistory.h" @@ -28,6 +32,8 @@ #include #include +#include "utils/ScopeGuard.h" + namespace { static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 5; static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30; @@ -101,6 +107,20 @@ bool Wallet::disconnected() const return m_disconnected; } +bool Wallet::refreshing() const +{ + return m_refreshing; +} + +void Wallet::refreshingSet(bool value) +{ + if (m_refreshing.exchange(value) != value) + { + emit refreshingChanged(); + } +} + + void Wallet::setConnectionStatus(ConnectionStatus value) { if (m_connectionStatus == value) @@ -245,7 +265,7 @@ void Wallet::initAsync( emit walletCreationHeightChanged(); qDebug() << "init async finished - starting refresh"; connected(true); - m_walletImpl->startRefresh(); + startRefresh(); } }); if (future.first) @@ -456,20 +476,14 @@ bool Wallet::importOutputs(const QString& path) { return m_walletImpl->importOutputs(path.toStdString()); } -bool Wallet::refresh() +void Wallet::startRefresh() { - qDebug() << "refresh async"; - m_walletImpl->refreshAsync(); + m_refreshEnabled = true; } -void Wallet::startRefresh() const +void Wallet::pauseRefresh() { - m_walletImpl->startRefresh(); -} - -void Wallet::pauseRefresh() const -{ - m_walletImpl->pauseRefresh(); + m_refreshEnabled = false; } PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id, @@ -580,6 +594,29 @@ bool Wallet::submitTxFile(const QString &fileName) const return m_walletImpl->importKeyImages(fileName.toStdString() + "_keyImages"); } +bool Wallet::refresh(bool historyAndSubaddresses /* = true */) +{ + refreshingSet(true); + const auto cleanup = sg::make_scope_guard([this]() noexcept { + refreshingSet(false); + }); + + { + QMutexLocker locker(&m_asyncMutex); + + bool result = m_walletImpl->refresh(); + if (historyAndSubaddresses) + { + m_history->refresh(currentSubaddressAccount()); + m_subaddress->refresh(currentSubaddressAccount()); + m_subaddressAccount->getAll(); + } + if (result) + emit updated(); + return result; + } +} + void Wallet::commitTransactionAsync(PendingTransaction *t) { m_scheduler.run([this, t] { @@ -1063,6 +1100,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) , m_subaddressAccount(nullptr) , m_subaddressAccountModel(nullptr) , m_coinsModel(nullptr) + , m_refreshEnabled(false) + , m_refreshing(false) , m_scheduler(this) { m_history = new TransactionHistory(m_walletImpl->history(), this); @@ -1081,12 +1120,17 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) m_connectionStatusRunning = false; m_daemonUsername = ""; m_daemonPassword = ""; + + startRefreshThread(); } Wallet::~Wallet() { qDebug("~Wallet: Closing wallet"); + pauseRefresh(); + m_walletImpl->stop(); + m_scheduler.shutdownWaitForFinished(); delete m_addressBook; @@ -1116,3 +1160,32 @@ Wallet::~Wallet() m_walletListener = NULL; qDebug("m_walletImpl deleted"); } + +void Wallet::startRefreshThread() +{ + const auto future = m_scheduler.run([this] { + constexpr const std::chrono::seconds refreshInterval{10}; + constexpr const std::chrono::milliseconds intervalResolution{100}; + + auto last = std::chrono::steady_clock::now(); + while (!m_scheduler.stopping()) + { + if (m_refreshEnabled) + { + const auto now = std::chrono::steady_clock::now(); + const auto elapsed = now - last; + if (elapsed >= refreshInterval) + { + refresh(false); + last = std::chrono::steady_clock::now(); + } + } + + std::this_thread::sleep_for(intervalResolution); + } + }); + if (!future.first) + { + throw std::runtime_error("failed to start auto refresh thread"); + } +} \ No newline at end of file diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 6cf1ef4..010009d 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -50,6 +50,7 @@ class Wallet : public QObject, public PassprasePrompter { Q_OBJECT Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged) + Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged) Q_PROPERTY(QString seed READ getSeed) Q_PROPERTY(QString seedLanguage READ getSeedLanguage) Q_PROPERTY(Status status READ status) @@ -199,11 +200,11 @@ public: Q_INVOKABLE bool importOutputs(const QString& path); //! refreshes the wallet - Q_INVOKABLE bool refresh(); + Q_INVOKABLE bool refresh(bool historyAndSubaddresses = false); // pause/resume refresh - Q_INVOKABLE void startRefresh() const; - Q_INVOKABLE void pauseRefresh() const; + Q_INVOKABLE void startRefresh(); + Q_INVOKABLE void pauseRefresh(); //! returns current wallet's block height //! (can be less than daemon's blockchain height when wallet sync in progress) @@ -411,6 +412,7 @@ signals: void currentSubaddressAccountChanged() const; void disconnectedChanged() const; void proxyAddressChanged() const; + void refreshingChanged() const; private: Wallet(QObject * parent = nullptr); @@ -428,9 +430,13 @@ private: const QString& proxyAddress); bool disconnected() const; + bool refreshing() const; + void refreshingSet(bool value); + void setConnectionStatus(ConnectionStatus value); QString getProxyAddress() const; void setProxyAddress(QString address); + void startRefreshThread(); private: friend class WalletManager;