connect(m_wallet->coins(), &Coins::descriptionChanged, [this] {
m_wallet->history()->refresh();
});
+
+ connect(m_wallet->coins(), &Coins::refreshStarted, [this]{
+ m_coinsRefreshing = true;
+ this->updateNetStats();
+ });
+
+ connect(m_wallet->coins(), &Coins::refreshFinished, [this]{
+ m_coinsRefreshing = false;
+ this->updateNetStats();
+ });
// Vice versa
connect(m_wallet->transactionHistoryModel(), &TransactionHistoryModel::transactionDescriptionChanged, [this] {
m_wallet->coins()->refresh();
if (m_wallet && m_wallet->lastSyncTime().isValid()) {
toolTip += QString("\nWallet synced: %1").arg(Utils::timeAgo(m_wallet->lastSyncTime()));
}
+
+ if (m_wallet) {
+ qint64 nextRefresh = m_wallet->secondsUntilNextRefresh();
+ if (nextRefresh > 0) {
+ toolTip += QString("\nNext sync attempt in: %1s").arg(nextRefresh);
+ } else if (nextRefresh == 0) {
+ toolTip += "\nSync attempt in progress...";
+ } else if (nextRefresh == -2) {
+ toolTip += "\nHardware wallet disconnected";
+ }
+ }
+
m_statusLabelBalance->setToolTip(toolTip);
this->updateSyncStatusToolTip();
if (lastSync.isValid()) {
qint64 secsSinceSync = lastSync.secsTo(QDateTime::currentDateTime());
- blocksBehindEstimated = secsSinceSync / 120; // ~2 min per block
+ if (secsSinceSync > 0) {
+ blocksBehindEstimated = secsSinceSync / 120; // ~2 min per block
+ }
}
quint64 blocksBehind = std::max(blocksBehindActual, blocksBehindEstimated);
// Estimate blocks behind based on time since last sync
if (m_wallet && m_wallet->lastSyncTime().isValid()) {
qint64 secsSinceLastSync = m_wallet->lastSyncTime().secsTo(QDateTime::currentDateTime());
- quint64 estimatedBlocksBehind = secsSinceLastSync / 120; // ~2 min per block
- if (estimatedBlocksBehind > 0) {
- statusStr = tr("~%1 blocks behind").arg(QLocale().toString(estimatedBlocksBehind));
- break;
+ if (secsSinceLastSync > 0) {
+ quint64 estimatedBlocksBehind = secsSinceLastSync / 120; // ~2 min per block
+ if (estimatedBlocksBehind > 0) {
+ statusStr = tr("~%1 blocks behind").arg(QLocale().toString(estimatedBlocksBehind));
+ break;
+ }
}
}
statusStr = "Disconnected";
void MainWindow::updateNetStats() {
if (!m_wallet || m_wallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected
- || m_wallet->connectionStatus() == Wallet::ConnectionStatus_Synchronized)
+ || (m_wallet->connectionStatus() == Wallet::ConnectionStatus_Synchronized && !m_coinsRefreshing))
{
m_statusLabelNetStats->hide();
return;
QString m_statusText;
int m_statusDots;
+ bool m_coinsRefreshing = false;
bool m_constructingTransaction = false;
bool m_statusOverrideActive = false;
bool m_showDeviceError = false;
// Proxy
connect(ui->proxyWidget, &NetworkProxyWidget::proxySettingsChanged, this, &Settings::onProxySettingsChanged);
+ // Offline mode
+ ui->checkBox_offlineMode->setChecked(conf()->get(Config::offlineMode).toBool());
+ connect(ui->checkBox_offlineMode, &QCheckBox::toggled, [this](bool checked){
+ conf()->set(Config::offlineMode, checked);
+ this->enableWebsocket(!checked && !conf()->get(Config::disableWebsocket).toBool());
+ emit offlineMode(checked);
+ });
+
// Websocket
// [Obtain third-party data]
ui->checkBox_enableWebsocket->setChecked(!conf()->get(Config::disableWebsocket).toBool());
, m_useSSL(true)
, m_coins(new Coins(this, wallet->getWallet(), this))
, m_storeTimer(new QTimer(this))
+ , m_lastRefreshTime(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch()).count())
{
m_walletListener = new WalletListenerImpl(this);
m_walletImpl->setListener(m_walletListener);
qInfo() << "Calling m_walletImpl->refresh(). Wallet height:" << walletHeight << "Daemon height:" << daemonHeight << "Target:" << targetHeight;
m_walletImpl->refresh();
}
+ m_lastRefreshTime = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
last = std::chrono::steady_clock::now();
}
}
return m_wallet2->get_blockchain_current_height();
}
+qint64 Wallet::secondsUntilNextRefresh() const {
+ if (m_syncPaused || !m_refreshEnabled) {
+ return -1;
+ }
+
+ if (this->isHwBacked() && !this->isDeviceConnected()) {
+ return -2;
+ }
+
+ auto now = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ auto elapsed = std::chrono::microseconds(now - m_lastRefreshTime.load());
+ auto interval = std::chrono::seconds(m_refreshInterval);
+
+ if (elapsed >= interval) {
+ return 0;
+ }
+
+ return std::chrono::duration_cast<std::chrono::seconds>(interval - elapsed).count();
+}
+
quint64 Wallet::daemonBlockChainHeight() const {
return m_daemonBlockChainHeight;
}
#include "rows/TxBacklogEntry.h"
#include <set>
+#include <atomic>
class WalletListenerImpl;
QDateTime lastSyncTime() const;
void setRefreshInterval(int seconds);
+ qint64 secondsUntilNextRefresh() const;
//! return true if deterministic keys
bool isDeterministic() const;
Coins *m_coins;
CoinsModel *m_coinsModel;
- int m_refreshInterval = 10;
+ std::atomic<int> m_refreshInterval{10};
QMutex m_asyncMutex;
QString m_daemonUsername;
std::atomic<quint64> m_stopHeight{0};
std::atomic<bool> m_rangeSyncActive{false};
std::atomic<bool> m_syncPaused{false};
+ std::atomic<int64_t> m_lastRefreshTime{0};
};
#endif // FEATHER_WALLET_H
this->setPercentageVisible(false);
connect(m_wallet, &Wallet::balanceUpdated, this, &BalanceTickerWidget::updateDisplay);
+ connect(m_wallet, &Wallet::connectionStatusChanged, this, &BalanceTickerWidget::updateDisplay);
connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &BalanceTickerWidget::updateDisplay);
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &BalanceTickerWidget::updateDisplay);
}
void BalanceTickerWidget::updateDisplay() {
- double balance = (m_totalBalance ? m_wallet->balanceAll() : m_wallet->balance()) / constants::cdiv;
+ double balance = (m_totalBalance ? m_wallet->balanceAll() : m_wallet->balance());
+ double balanceAmount = balance / constants::cdiv;
QString fiatCurrency = conf()->get(Config::preferredFiatCurrency).toString();
- double balanceFiatAmount = appData()->prices.convert("XMR", fiatCurrency, balance);
+ double balanceFiatAmount = appData()->prices.convert("XMR", fiatCurrency, balanceAmount);
bool isCacheValid = appData()->prices.lastUpdateTime.isValid();
bool isCacheFresh = isCacheValid && appData()->prices.lastUpdateTime.secsTo(QDateTime::currentDateTime()) < 3600;
bool hasXmrPrice = appData()->prices.markets.contains("XMR");
bool hasFiatRate = fiatCurrency == "USD" || appData()->prices.rates.contains(fiatCurrency);
- if (balanceFiatAmount == 0.0 || !isCacheValid) {
+ if (balance > 0 && (balanceFiatAmount == 0.0 || !isCacheValid)) {
if (conf()->get(Config::offlineMode).toBool() || conf()->get(Config::disableWebsocket).toBool() || m_wallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected) {
this->setDisplayText("offline");
} else if (!hasXmrPrice || !hasFiatRate) {