]> Nutra Git (v2) - gamesguru/feather.git/commitdiff
add dedicated SyncRangeDialog. Add old Ubuntu CI
authorgg <chown_tee@proton.me>
Wed, 14 Jan 2026 16:46:42 +0000 (11:46 -0500)
committergg <chown_tee@proton.me>
Wed, 14 Jan 2026 17:16:56 +0000 (12:16 -0500)
.github/workflows/build.yml
src/MainWindow.cpp
src/MainWindow.h
src/dialog/SyncRangeDialog.cpp [new file with mode: 0644]
src/dialog/SyncRangeDialog.h [new file with mode: 0644]
src/libwalletqt/Wallet.cpp
src/libwalletqt/Wallet.h
src/utils/WebsocketClient.cpp
src/utils/nodes.cpp

index 4034c64d15d965d752c28e8b14ae3dc948d99bbf..07dc6c0740e063a6380899b52b07bc5a60d31264 100644 (file)
@@ -1,6 +1,8 @@
 name: ci/gh-actions/build
 
-on: [pull_request]
+on:
+  push: [master, main, dev]
+  pull_request:
 
 jobs:
   build-ubuntu-without-scanner:
@@ -30,6 +32,65 @@ jobs:
           cmake -DWITH_SCANNER=OFF ..
           cmake --build . -j $(nproc)
 
+  build-ubuntu-22:
+    name: "Ubuntu 22.04"
+    runs-on: ubuntu-latest
+    container:
+      image: ubuntu:22.04
+    steps:
+      - name: update apt
+        run: apt update
+      - name: install dependencies
+        run:
+          apt -y install git cmake build-essential ccache libssl-dev libunbound-dev libboost-all-dev
+          libqrencode-dev qt6-base-dev qt6-svg-dev qt6-websockets-dev qt6-multimedia-dev
+          libzip-dev libsodium-dev libgcrypt20-dev libx11-xcb-dev
+          protobuf-compiler libprotobuf-dev libhidapi-dev libusb-dev
+          libusb-1.0-0-dev
+      - name: configure git
+        run: git config --global --add safe.directory '*'
+      - uses: actions/checkout@v4
+        with:
+          submodules: recursive
+      - name: build
+        run: |
+          mkdir build
+          cd build
+          cmake -DWITH_SCANNER=OFF ..
+          cmake --build . -j $(nproc)
+
+  build-ubuntu-20:
+    name: "Ubuntu 20.04"
+    runs-on: ubuntu-latest
+    container:
+      image: ubuntu:20.04
+    env:
+      DEBIAN_FRONTEND: noninteractive
+    steps:
+      - name: update apt
+        run: apt update
+      - name: install dependencies
+        run: |
+          apt -y install software-properties-common
+          add-apt-repository -y ppa:beineri/opt-qt-5.15.2-focal
+          apt update
+          apt -y install git cmake build-essential ccache libssl-dev libunbound-dev libboost-all-dev \
+          libqrencode-dev qt515base qt515svg qt515websockets qt515multimedia \
+          libzip-dev libsodium-dev libgcrypt20-dev libx11-xcb-dev \
+          protobuf-compiler libprotobuf-dev libhidapi-dev libusb-dev libusb-1.0-0-dev
+      - name: configure git
+        run: git config --global --add safe.directory '*'
+      - uses: actions/checkout@v4
+        with:
+          submodules: recursive
+      - name: build
+        run: |
+          source /opt/qt515/bin/qt515-env.sh
+          mkdir build
+          cd build
+          cmake -DWITH_SCANNER=OFF ..
+          cmake --build . -j $(nproc)
+
   build-arch:
     name: "Arch Linux"
     runs-on: ubuntu-latest
index 242d1de9dc615c7f6213a4af0c738a5353996c93..d96ea9757db676e0ca4cbd259443fd7a02ddd520 100644 (file)
@@ -14,6 +14,7 @@
 #include <QFormLayout>
 #include <QSpinBox>
 #include <QDateEdit>
+#include <QComboBox>
 #include <QDialogButtonBox>
 
 #include "constants.h"
@@ -31,6 +32,7 @@
 #include "dialog/ViewOnlyDialog.h"
 #include "dialog/WalletInfoDialog.h"
 #include "dialog/WalletCacheDebugDialog.h"
+#include "dialog/SyncRangeDialog.h"
 #include "libwalletqt/AddressBook.h"
 #include "libwalletqt/rows/CoinsInfo.h"
 #include "libwalletqt/rows/Output.h"
@@ -119,6 +121,7 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa
 
     // Timers
     connect(&m_updateBytes, &QTimer::timeout, this, &MainWindow::updateNetStats);
+    connect(&m_updateBytes, &QTimer::timeout, this, &MainWindow::updateStatusToolTip);
     connect(&m_txTimer, &QTimer::timeout, [this]{
         QString text = "Constructing transaction" + this->statusDots();
         m_statusLabelStatus->setText(text);
@@ -291,99 +294,15 @@ void MainWindow::initStatusBar() {
     connect(syncRangeAction, &QAction::triggered, this, [this](){
         if (!m_wallet) return;
 
-        QDialog dialog(this);
-        dialog.setWindowTitle(tr("Sync Date Range"));
-        dialog.setWindowIcon(QIcon(":/assets/images/appicons/64x64.png"));
-        dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
-        dialog.setWindowFlags(dialog.windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
-
-        auto *layout = new QVBoxLayout(&dialog);
-
-        auto *formLayout = new QFormLayout;
-        formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
-
-        auto *toDateEdit = new QDateEdit(QDate::currentDate());
-        toDateEdit->setCalendarPopup(true);
-        toDateEdit->setDisplayFormat("yyyy-MM-dd");
-
-        // Load lookup for accurate block calculations
-        NetworkType::Type nettype = m_wallet->nettype();
-        QString filename = Utils::getRestoreHeightFilename(nettype);
-
-        std::unique_ptr<RestoreHeightLookup> lookup(RestoreHeightLookup::fromFile(filename, nettype));
-
-        int defaultDays = 7;
-
-        auto *daysSpinBox = new QSpinBox;
-        daysSpinBox->setRange(1, 3650); // 10 years
-        daysSpinBox->setValue(defaultDays);
-        daysSpinBox->setSuffix(tr(" days"));
-
-        auto *fromDateEdit = new QDateEdit(QDate::currentDate().addDays(-defaultDays));
-        fromDateEdit->setCalendarPopup(true);
-        fromDateEdit->setDisplayFormat("yyyy-MM-dd");
-        fromDateEdit->setToolTip(tr("Calculated from 'End date' and day span."));
-
-        auto *infoLabel = new QLabel;
-        infoLabel->setWordWrap(true);
-        infoLabel->setStyleSheet("QLabel { color: #888; font-size: 11px; }");
-
-        formLayout->addRow(tr("Day span:"), daysSpinBox);
-        formLayout->addRow(tr("Start date:"), fromDateEdit);
-        formLayout->addRow(tr("End date:"), toDateEdit);
-
-        layout->addLayout(formLayout);
-        layout->addWidget(infoLabel);
-
-        auto updateInfo = [=, &lookup]() {
-            QDate start = fromDateEdit->date();
-            QDate end = toDateEdit->date();
-
-            uint64_t startHeight = lookup->dateToHeight(start.startOfDay().toSecsSinceEpoch());
-            uint64_t endHeight = lookup->dateToHeight(end.endOfDay().toSecsSinceEpoch());
-
-            if (endHeight < startHeight) endHeight = startHeight;
-            quint64 blocks = endHeight - startHeight;
-            quint64 size = Utils::estimateSyncDataSize(blocks);
-
-            infoLabel->setText(tr("Scanning ~%1 blocks\nEst. download size: %2")
-                               .arg(blocks)
-                               .arg(Utils::formatBytes(size)));
-        };
-
-        auto updateFromDate = [=]() {
-            fromDateEdit->setDate(toDateEdit->date().addDays(-daysSpinBox->value()));
-            updateInfo();
-        };
-
-        connect(fromDateEdit, &QDateEdit::dateChanged, updateInfo);
-        connect(toDateEdit, &QDateEdit::dateChanged, updateFromDate);
-        connect(daysSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), updateFromDate);
-
-        // Init label
-        updateInfo();
-
-        auto *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
-        connect(btnBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
-        connect(btnBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
-        layout->addWidget(btnBox);
-
-        dialog.resize(320, dialog.height());
-
+        SyncRangeDialog dialog(this, m_wallet);
         if (dialog.exec() == QDialog::Accepted) {
-            m_wallet->syncDateRange(fromDateEdit->date(), toDateEdit->date());
-
-            // Re-calculate for status text
-            uint64_t startHeight = lookup->dateToHeight(fromDateEdit->date().startOfDay().toSecsSinceEpoch());
-            uint64_t endHeight = lookup->dateToHeight(toDateEdit->date().endOfDay().toSecsSinceEpoch());
-            quint64 blocks = (endHeight > startHeight) ? endHeight - startHeight : 0;
-            quint64 size = Utils::estimateSyncDataSize(blocks);
+            m_wallet->syncDateRange(dialog.fromDate(), dialog.toDate());
 
             this->setStatusText(tr("Syncing range %1 - %2 (~%3 blocks)\nEst. download size: %4")
-                                .arg(fromDateEdit->date().toString("yyyy-MM-dd"))
-                                .arg(toDateEdit->date().toString("yyyy-MM-dd"))
-                                .arg(QLocale().toString(blocks))
-                                .arg(Utils::formatBytes(size)));
+                                .arg(dialog.fromDate().toString("yyyy-MM-dd"))
+                                .arg(dialog.toDate().toString("yyyy-MM-dd"))
+                                .arg(QLocale().toString(dialog.estimatedBlocks()))
+                                .arg(Utils::formatBytes(dialog.estimatedSize())));
         }
     });
 
@@ -413,7 +332,11 @@ void MainWindow::initStatusBar() {
 
             if (QMessageBox::question(this, tr("Full Sync"), msg) == QMessageBox::Yes) {
                 m_wallet->fullSync();
-                this->setStatusText(tr("Full sync started (%1 blocks)...").arg(estBlocks));
+                if (estBlocks.startsWith("Unknown")) {
+                    this->setStatusText(tr("Full sync started..."));
+                } else {
+                    this->setStatusText(tr("Full sync started (%1 blocks)...").arg(estBlocks));
+                }
             }
         }
     });
@@ -897,11 +820,8 @@ void MainWindow::onBalanceUpdated(quint64 balance, quint64 spendable) {
         }
     }
 
-    QString toolTip = "Right-click for details";
-    if (appData()->prices.lastUpdateTime.isValid()) {
-        toolTip += QString("\nLast updated: %1").arg(Utils::timeAgo(appData()->prices.lastUpdateTime));
-    }
-    m_statusLabelBalance->setToolTip(toolTip);
+    this->updateStatusToolTip();
+
 
     QString finalText = "Balance: " + valueStr + suffixStr;
     qDebug() << "Setting balance label text:" << finalText;
@@ -910,6 +830,16 @@ void MainWindow::onBalanceUpdated(quint64 balance, quint64 spendable) {
     m_statusLabelBalance->setProperty("copyableValue", valueStr);
 }
 
+void MainWindow::updateStatusToolTip() {
+    QString toolTip = "Right-click for details";
+    if (appData()->prices.lastUpdateTime.isValid()) {
+        toolTip += QString("\nPrice updated: %1").arg(Utils::timeAgo(appData()->prices.lastUpdateTime));
+    }
+    if (m_wallet->lastSyncTime().isValid()) {
+        toolTip += QString("\nWallet synced: %1").arg(Utils::timeAgo(m_wallet->lastSyncTime()));
+    }
+    m_statusLabelBalance->setToolTip(toolTip);
+}
 
 void MainWindow::setStatusText(const QString &text, bool override, int timeout) {
 
@@ -1028,19 +958,24 @@ void MainWindow::onMultiBroadcast(const QMap<QString, QString> &txHexMap) {
 
 void MainWindow::onSyncStatus(quint64 height, quint64 target, bool daemonSync) {
     qDebug() << "onSyncStatus: Height" << height << "Target" << target << "DaemonSync" << daemonSync;
+
+    quint64 blocksBehind = Utils::blocksBehind(height, target);
+    m_lastSyncStatusUpdate = QDateTime::currentDateTime();
+
     if (height >= (target - 1) && target > 0) {
         this->updateNetStats();
         this->setStatusText(QString("Synchronized (%1)").arg(QLocale().toString(height)));
     } else {
-        quint64 blocksBehind = Utils::blocksBehind(height, target);
         QString type = daemonSync ? tr("Blockchain") : tr("Wallet");
         QString blocksStr = QLocale().toString(blocksBehind);
         this->setStatusText(tr("%1 sync: %2 blocks behind").arg(type, blocksStr));
     }
-    m_lastSyncStatusUpdate = QDateTime::currentDateTime();
-    QString tooltip = tr("Wallet Height: %1 | Network Tip: %2\nLast updated: %3")
+
+    QString syncStatus = blocksBehind > 0 ? tr("%1 blocks behind").arg(QLocale().toString(blocksBehind)) : tr("Synchronized");
+    QString tooltip = tr("Wallet Height: %1 | Network Tip: %2\n%3\nLast updated: %4")
         .arg(QLocale().toString(height))
         .arg(QLocale().toString(target))
+        .arg(syncStatus)
         .arg(m_lastSyncStatusUpdate.toString("HH:mm:ss"));
 
     qDebug() << "Setting Status Tooltip:" << tooltip;
index d5d1a862bab61d89b0c12aa7cad2621eb9e03f7e..37c86090fb27c8e3d323e211a20f1ca57d2992a8 100644 (file)
@@ -207,6 +207,7 @@ private:
     void fillSendTab(const QString &address, const QString &description);
     void userActivity();
     void checkUserActivity();
+    void updateStatusToolTip();
     void lockWallet();
     void unlockWallet(const QString &password);
     void closeQDialogChildren(QObject *object);
diff --git a/src/dialog/SyncRangeDialog.cpp b/src/dialog/SyncRangeDialog.cpp
new file mode 100644 (file)
index 0000000..15e2971
--- /dev/null
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: The Monero Project
+
+#include "SyncRangeDialog.h"
+
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QFormLayout>
+#include <QComboBox>
+#include <QSpinBox>
+#include <QDateEdit>
+#include <QLabel>
+#include <QDialogButtonBox>
+
+#include "utils/Utils.h"
+#include "utils/RestoreHeightLookup.h"
+
+SyncRangeDialog::SyncRangeDialog(QWidget *parent, Wallet *wallet)
+    : QDialog(parent)
+    , m_wallet(wallet)
+{
+    setWindowTitle(tr("Sync Date Range"));
+    setWindowIcon(QIcon(":/assets/images/appicons/64x64.png"));
+    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+    setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
+
+    auto *layout = new QVBoxLayout(this);
+    auto *formLayout = new QFormLayout;
+    formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+
+    m_toDateEdit = new QDateEdit(QDate::currentDate());
+    m_toDateEdit->setCalendarPopup(true);
+    m_toDateEdit->setDisplayFormat("yyyy-MM-dd");
+
+    int defaultDays = 7;
+
+    // Preset durations dropdown
+    m_presetCombo = new QComboBox;
+    m_presetCombo->addItem(tr("1 day"), 1);
+    m_presetCombo->addItem(tr("7 days"), 7);
+    m_presetCombo->addItem(tr("30 days"), 30);
+    m_presetCombo->addItem(tr("90 days"), 90);
+    m_presetCombo->addItem(tr("1 year"), 365);
+    m_presetCombo->addItem(tr("Custom..."), -1);
+    m_presetCombo->setCurrentIndex(1); // Default to 7 days
+
+    m_daysSpinBox = new QSpinBox;
+    m_daysSpinBox->setRange(1, 3650); // 10 years
+    m_daysSpinBox->setValue(defaultDays);
+    m_daysSpinBox->setSuffix(tr(" days"));
+    m_daysSpinBox->setVisible(false); // Hidden until "Custom..." is selected
+
+    // Layout for preset + custom spinbox
+    auto *daysLayout = new QHBoxLayout;
+    daysLayout->setContentsMargins(0, 0, 0, 0);
+    daysLayout->addWidget(m_presetCombo, 1);
+    daysLayout->addWidget(m_daysSpinBox, 0);
+
+    m_fromDateEdit = new QDateEdit(QDate::currentDate().addDays(-defaultDays));
+    m_fromDateEdit->setCalendarPopup(true);
+    m_fromDateEdit->setDisplayFormat("yyyy-MM-dd");
+    m_fromDateEdit->setToolTip(tr("Calculated from 'End date' and day span."));
+
+    m_infoLabel = new QLabel;
+    m_infoLabel->setWordWrap(true);
+    m_infoLabel->setStyleSheet("QLabel { color: #888; font-size: 11px; }");
+
+    formLayout->addRow(tr("Day span:"), daysLayout);
+    formLayout->addRow(tr("Start date:"), m_fromDateEdit);
+    formLayout->addRow(tr("End date:"), m_toDateEdit);
+
+    layout->addLayout(formLayout);
+    layout->addWidget(m_infoLabel);
+
+    connect(m_fromDateEdit, &QDateEdit::dateChanged, this, &SyncRangeDialog::updateInfo);
+    connect(m_toDateEdit, &QDateEdit::dateChanged, this, &SyncRangeDialog::updateFromDate);
+    connect(m_daysSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &SyncRangeDialog::updateFromDate);
+
+    // Connect preset dropdown
+    connect(m_presetCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
+        int days = m_presetCombo->itemData(index).toInt();
+        if (days == -1) {
+            // Custom mode: show spinbox, keep current value
+            m_daysSpinBox->setVisible(true);
+        } else {
+            // Preset mode: hide spinbox, set value
+            m_daysSpinBox->setVisible(false);
+            m_daysSpinBox->setValue(days);
+        }
+    });
+
+    // Init info
+    updateInfo();
+
+    auto *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+    connect(btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+    connect(btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+    layout->addWidget(btnBox);
+
+    resize(320, height());
+}
+
+QDate SyncRangeDialog::fromDate() const {
+    return m_fromDateEdit->date();
+}
+
+QDate SyncRangeDialog::toDate() const {
+    return m_toDateEdit->date();
+}
+
+quint64 SyncRangeDialog::estimatedBlocks() const {
+    return m_estimatedBlocks;
+}
+
+quint64 SyncRangeDialog::estimatedSize() const {
+    return m_estimatedSize;
+}
+
+void SyncRangeDialog::updateInfo() {
+    NetworkType::Type nettype = m_wallet->nettype();
+    QString filename = Utils::getRestoreHeightFilename(nettype);
+    std::unique_ptr<RestoreHeightLookup> lookup(RestoreHeightLookup::fromFile(filename, nettype));
+
+    QDate start = m_fromDateEdit->date();
+    QDate end = m_toDateEdit->date();
+
+    uint64_t startHeight = lookup->dateToHeight(start.startOfDay().toSecsSinceEpoch());
+    uint64_t endHeight = lookup->dateToHeight(end.endOfDay().toSecsSinceEpoch());
+
+    if (endHeight < startHeight) endHeight = startHeight;
+    m_estimatedBlocks = endHeight - startHeight;
+    m_estimatedSize = Utils::estimateSyncDataSize(m_estimatedBlocks);
+
+    m_infoLabel->setText(tr("Scanning ~%1 blocks\nEst. download size: %2")
+                           .arg(m_estimatedBlocks)
+                           .arg(Utils::formatBytes(m_estimatedSize)));
+}
+
+void SyncRangeDialog::updateFromDate() {
+    m_fromDateEdit->setDate(m_toDateEdit->date().addDays(-m_daysSpinBox->value()));
+    updateInfo();
+}
diff --git a/src/dialog/SyncRangeDialog.h b/src/dialog/SyncRangeDialog.h
new file mode 100644 (file)
index 0000000..baa9294
--- /dev/null
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-FileCopyrightText: The Monero Project
+
+#ifndef FEATHER_SYNCRANGEDIALOG_H
+#define FEATHER_SYNCRANGEDIALOG_H
+
+#include <QDialog>
+#include <QDate>
+
+#include "libwalletqt/Wallet.h"
+
+class QComboBox;
+class QSpinBox;
+class QDateEdit;
+class QLabel;
+
+class SyncRangeDialog : public QDialog
+{
+Q_OBJECT
+
+public:
+    explicit SyncRangeDialog(QWidget *parent, Wallet *wallet);
+    ~SyncRangeDialog() override = default;
+
+    QDate fromDate() const;
+    QDate toDate() const;
+    quint64 estimatedBlocks() const;
+    quint64 estimatedSize() const;
+
+private:
+    void updateInfo();
+    void updateFromDate();
+
+    Wallet *m_wallet;
+    QComboBox *m_presetCombo;
+    QSpinBox *m_daysSpinBox;
+    QDateEdit *m_fromDateEdit;
+    QDateEdit *m_toDateEdit;
+    QLabel *m_infoLabel;
+
+    quint64 m_estimatedBlocks = 0;
+    quint64 m_estimatedSize = 0;
+};
+
+#endif //FEATHER_SYNCRANGEDIALOG_H
index 8aadcb813f3ba68f6f1b96b691a0ec82ea307aa7..f28424ac657b391ab62e23c1dd4133ce82a45429 100644 (file)
@@ -418,6 +418,15 @@ void Wallet::setDaemonLogin(const QString &daemonUsername, const QString &daemon
 void Wallet::initAsync(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, const QString &proxyAddress)
 {
     qDebug() << "initAsync: " + daemonAddress;
+
+    if (daemonAddress.isEmpty()) {
+        m_scheduler.run([this] {
+            m_wallet2->set_offline(true);
+        });
+        setConnectionStatus(Wallet::ConnectionStatus_Disconnected);
+        return;
+    }
+
     const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, proxyAddress] {
         // Beware! This code does not run in the GUI thread.
 
@@ -567,6 +576,10 @@ void Wallet::onHeightsRefreshed(bool success, quint64 daemonHeight, quint64 targ
     } else {
         setConnectionStatus(ConnectionStatus_Disconnected);
     }
+
+    if (success) {
+        m_lastSyncTime = QDateTime::currentDateTime();
+    }
 }
 
 quint64 Wallet::blockChainHeight() const {
@@ -591,6 +604,10 @@ void Wallet::setSyncPaused(bool paused) {
     }
 }
 
+QDateTime Wallet::lastSyncTime() const {
+    return m_lastSyncTime;
+}
+
 void Wallet::skipToTip() {
     if (!m_wallet2)
         return;
index 0e62d0bbea7e5de3ec32d1ac69228950eed3a0b1..099ded2bca8b6cbc98047606842235cec672bfb4 100644 (file)
@@ -138,6 +138,8 @@ public:
     //! returns if view only wallet
     bool viewOnly() const;
 
+    QDateTime lastSyncTime() const;
+
     //! return true if deterministic keys
     bool isDeterministic() const;
 
@@ -504,6 +506,7 @@ private:
 
     quint64 m_daemonBlockChainHeight;
     quint64 m_daemonBlockChainTargetHeight;
+    QDateTime m_lastSyncTime;
 
     ConnectionStatus m_connectionStatus;
 
index 45b44799254bf33ccfbd3dd7c95d98ec7473e8f6..a894e177f4bd646f50f5b13f91a9c2bd60a19670 100644 (file)
@@ -83,7 +83,6 @@ void WebsocketClient::restart() {
 void WebsocketClient::stop() {
     qDebug() << Q_FUNC_INFO;
     m_stopped = true;
-    webSocket->disconnect();
     webSocket->abort();
     m_connectionTimeout.stop();
     m_pingTimer.stop();
index 38dbdb74798b254341e7a8b8b7b8c32a69675655..4b55fb92fa3a4e0ccf2055f1077fc7c92bf2f4af 100644 (file)
@@ -551,7 +551,9 @@ void Nodes::resetLocalState() {
 }
 
 void Nodes::exhausted() {
-    // Do nothing
+    // All nodes have been tried and failed - clear the failure list to allow a new retry cycle
+    qInfo() << "All nodes exhausted, clearing recent failures to retry";
+    m_recentFailures.clear();
 }
 
 QList<FeatherNode> Nodes::nodes() {