wowlet: first-run 'be your own node' welcome + deferred startup + non-blocking node toggle. On first launch a welcome dialog lets the user choose own-node vs remote BEFORE any node starts (gated on firstRun; later launches use the saved choice); node start moved past it in WindowManager. Settings toggle now stops wownerod non-blocking (no UI freeze while it flushes LMDB). Copy: 'many recommended', no em-dash

This commit is contained in:
jwinterm
2026-06-19 16:02:17 -04:00
parent 487875ee9e
commit 3eab1e14fa
6 changed files with 66 additions and 12 deletions
+1 -1
View File
@@ -176,7 +176,7 @@ void Settings::setupNetworkTab() {
if (enabled) if (enabled)
daemonManager()->start(); daemonManager()->start();
else else
daemonManager()->stop(); daemonManager()->stop(false); // non-blocking: don't freeze Settings while wownerod flushes
m_nodes->autoConnect(true); m_nodes->autoConnect(true);
}); });
} else { } else {
+56 -5
View File
@@ -3,9 +3,15 @@
#include "WindowManager.h" #include "WindowManager.h"
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QFont>
#include <QInputDialog> #include <QInputDialog>
#include <QLabel>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWindow> #include <QWindow>
#include "Application.h" #include "Application.h"
@@ -48,16 +54,22 @@ WindowManager::WindowManager(QObject *parent)
this->buildTrayMenu(); this->buildTrayMenu();
m_tray->setVisible(conf()->get(Config::showTrayIcon).toBool()); m_tray->setVisible(conf()->get(Config::showTrayIcon).toBool());
// wowlet: boot the embedded wownerod node early so it syncs while the user opens their wallet.
// No-op if the user disabled the local node (Config::runLocalNode) or no daemon is bundled.
daemonManager()->init();
daemonManager()->start();
this->initSkins(); this->initSkins();
this->patchMacStylesheet(); this->patchMacStylesheet();
this->showCrashLogs(); this->showCrashLogs();
// wowlet: on the very first launch, let the user choose their own node vs a remote one BEFORE anything
// connects. No node — local or remote — starts until they hit Continue. Later launches use the saved choice.
if (conf()->get(Config::firstRun).toBool()) {
this->showNodeWelcome();
}
// wowlet: boot the embedded wownerod (shouldStartDaemon() makes this a no-op if the user chose a remote
// node), so it syncs while the user opens their wallet.
daemonManager()->init();
daemonManager()->start();
// wowlet: the embedded local node is configured automatically — skip the first-run network wizard. // wowlet: the embedded local node is configured automatically — skip the first-run network wizard.
this->onInitialNetworkConfigured(); this->onInitialNetworkConfigured();
@@ -68,6 +80,45 @@ WindowManager::WindowManager(QObject *parent)
} }
} }
// wowlet: first-run "be your own node" welcome. Shown before any node starts; records the user's choice
// in Config::runLocalNode, which start()/autoConnect then honour.
void WindowManager::showNodeWelcome() {
QDialog dlg;
dlg.setWindowTitle("Welcome to wowlet");
dlg.setMinimumWidth(440);
auto *title = new QLabel("🐕 Welcome to wowlet", &dlg);
QFont tf = title->font();
tf.setPointSize(tf.pointSize() + 4);
tf.setBold(true);
title->setFont(tf);
auto *intro = new QLabel("wowlet runs your own Wownero node by default: be your own boss and help run the network.", &dlg);
intro->setWordWrap(true);
auto *check = new QCheckBox("Run your own Wownero node (many recommended)", &dlg);
check->setChecked(conf()->get(Config::runLocalNode).toBool());
auto *hint = new QLabel("(untick to use a remote node instead)", &dlg);
hint->setStyleSheet("color: gray;");
auto *btn = new QPushButton("Continue", &dlg);
btn->setDefault(true);
connect(btn, &QPushButton::clicked, &dlg, &QDialog::accept);
auto *layout = new QVBoxLayout(&dlg);
layout->addWidget(title);
layout->addWidget(intro);
layout->addSpacing(8);
layout->addWidget(check);
layout->addWidget(hint);
layout->addSpacing(8);
layout->addWidget(btn, 0, Qt::AlignRight);
dlg.exec(); // modal — blocks until Continue
conf()->set(Config::runLocalNode, check->isChecked());
}
QPointer<WindowManager> WindowManager::m_instance(nullptr); QPointer<WindowManager> WindowManager::m_instance(nullptr);
void WindowManager::setEventFilter(EventFilter *ef) { void WindowManager::setEventFilter(EventFilter *ef) {
+1
View File
@@ -80,6 +80,7 @@ private:
bool autoOpenWallet(); bool autoOpenWallet();
void initWizard(); void initWizard();
void showNodeWelcome(); // wowlet: first-run "be your own node" choice, before any node starts
WalletWizard* createWizard(WalletWizard::Page startPage); WalletWizard* createWizard(WalletWizard::Page startPage);
void handleWalletError(const Utils::Message &message); void handleWalletError(const Utils::Message &message);
+4 -2
View File
@@ -97,7 +97,7 @@ void DaemonManager::start() {
m_started = true; m_started = true;
} }
void DaemonManager::stop() { void DaemonManager::stop(bool wait) {
m_stopping = true; m_stopping = true;
if (m_process->state() == QProcess::ProcessState::NotRunning) { if (m_process->state() == QProcess::ProcessState::NotRunning) {
m_started = false; m_started = false;
@@ -107,12 +107,14 @@ void DaemonManager::stop() {
// wownerod owns an LMDB database. Send SIGTERM so it flushes and exits cleanly; only hard-kill // wownerod owns an LMDB database. Send SIGTERM so it flushes and exits cleanly; only hard-kill
// if it refuses to shut down, to avoid blockchain database corruption. // if it refuses to shut down, to avoid blockchain database corruption.
m_process->terminate(); m_process->terminate();
m_started = false;
if (!wait)
return; // toggle/live use: let it flush + exit in the background instead of freezing the UI thread.
if (!m_process->waitForFinished(30000)) { if (!m_process->waitForFinished(30000)) {
qWarning() << "wownerod did not exit gracefully within 30s; killing"; qWarning() << "wownerod did not exit gracefully within 30s; killing";
m_process->kill(); m_process->kill();
m_process->waitForFinished(5000); m_process->waitForFinished(5000);
} }
m_started = false;
} }
void DaemonManager::onStateChanged(QProcess::ProcessState state) { void DaemonManager::onStateChanged(QProcess::ProcessState state) {
+1 -1
View File
@@ -26,7 +26,7 @@ public:
void init(); void init();
void start(); void start();
void stop(); void stop(bool wait = true); // wait=false: non-blocking (don't freeze the UI on a settings toggle)
bool unpackBins(); bool unpackBins();
bool isStarted() const { return m_started; } bool isStarted() const { return m_started; }
+3 -3
View File
@@ -59,11 +59,11 @@ NodeWidget::NodeWidget(QWidget *parent)
// wowlet: built-in node toggle + the "why run your own node" messaging. Running your own node is // wowlet: built-in node toggle + the "why run your own node" messaging. Running your own node is
// a privacy win (no remote operator sees your wallet) AND it strengthens the Wownero network by // a privacy win (no remote operator sees your wallet) AND it strengthens the Wownero network by
// adding a peer — a deliberate differentiator we surface clearly here. // adding a peer — a deliberate differentiator we surface clearly here.
m_localNodeCheck = new QCheckBox(tr("Run your own Wownero node (much recommended)"), this); m_localNodeCheck = new QCheckBox(tr("Run your own Wownero node (many recommended)"), this);
m_localNodeCheck->setChecked(conf()->get(Config::runLocalNode).toBool()); m_localNodeCheck->setChecked(conf()->get(Config::runLocalNode).toBool());
auto *localNodeInfo = new QLabel( auto *localNodeInfo = new QLabel(
tr("wowlet bundles its own node so you can be your own boss — and every node keeps the Wownero " tr("wowlet runs your own Wownero node by default: be your own boss and help run the network. "
"network strong + decentralized. 🐕 Uncheck to use a remote node instead."), this); "🐕 Uncheck to use a remote node instead."), this);
localNodeInfo->setWordWrap(true); localNodeInfo->setWordWrap(true);
localNodeInfo->setStyleSheet("color: gray;"); localNodeInfo->setStyleSheet("color: gray;");