feat(mining): persistent start/stop error caption + timeout-aware pool status
- A mining start/stop/pool failure now sets a persistent App::mining_last_error_ (cleared on the next attempt), rendered as a red caption with "Press Mine to try again" below the Mine button (plus a Dismiss, and a Windows-Defender "How to fix" on an AV block), so a crashed/blocked/failed miner is no longer just a button that "did nothing" once its toast faded. Two more hardcoded English strings (pool-payout-invalid, AV-blocked) moved to i18n. - Pool connection status is now a persistent "Status" row in the pool stats card (PoolMiningState.running_since / last_error): it flips from "Connecting…" to "Can't reach pool — check the URL" after a 30s timeout instead of sitting on red forever, and no longer vanishes once logs arrive. The old transient banner in mining_tab.cpp was removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+19
-8
@@ -3529,6 +3529,7 @@ void App::startMining(int threads)
|
||||
const int maxThreads = std::max(1, (int)std::thread::hardware_concurrency());
|
||||
threads = std::clamp(threads, 1, maxThreads);
|
||||
if (mining_toggle_in_progress_.exchange(true)) return; // already in progress
|
||||
clearMiningError(); // a fresh attempt wipes any stale error
|
||||
|
||||
worker_->post([this, threads]() -> rpc::RPCWorker::MainCb {
|
||||
bool ok = false;
|
||||
@@ -3548,7 +3549,9 @@ void App::startMining(int threads)
|
||||
DEBUG_LOGF("Mining started with %d threads\n", threads);
|
||||
} else {
|
||||
DEBUG_LOGF("Failed to start mining: %s\n", errMsg.c_str());
|
||||
ui::Notifications::instance().error(std::string(TR("toast_mining_failed")) + util::friendlyRpcError(errMsg));
|
||||
const std::string merr = std::string(TR("toast_mining_failed")) + util::friendlyRpcError(errMsg);
|
||||
setMiningError(merr);
|
||||
ui::Notifications::instance().error(merr);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -3559,6 +3562,7 @@ void App::stopMining()
|
||||
if (!supportsSoloMining()) return;
|
||||
if (!state_.connected || !rpc_ || !worker_) return;
|
||||
if (mining_toggle_in_progress_.exchange(true)) return; // already in progress
|
||||
clearMiningError();
|
||||
|
||||
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||
bool ok = false;
|
||||
@@ -3580,7 +3584,9 @@ void App::stopMining()
|
||||
} else {
|
||||
// Don't silently leave generate=true as if it worked: tell the user and let the next
|
||||
// getmininginfo refresh reconcile the true daemon state. (M-07)
|
||||
ui::Notifications::instance().error(std::string(TR("toast_stop_mining_failed")) + util::friendlyRpcError(errMsg));
|
||||
const std::string merr = std::string(TR("toast_stop_mining_failed")) + util::friendlyRpcError(errMsg);
|
||||
setMiningError(merr);
|
||||
ui::Notifications::instance().error(merr);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -3634,9 +3640,11 @@ void App::postPoolMinerJob(int threads, bool retargetOnly)
|
||||
// A pool pays with ordinary transactions, so only a shielded address can be paid (private chain).
|
||||
if (!cfg.worker_name.empty() && cfg.worker_name != "x" &&
|
||||
!util::isValidRecipientAddress(cfg.worker_name)) {
|
||||
ui::Notifications::instance().error(util::isTransparentAddress(cfg.worker_name)
|
||||
? TR("mining_payout_needs_shielded")
|
||||
: "Pool payout address is not a valid DragonX address — mining not started.");
|
||||
const std::string merr = util::isTransparentAddress(cfg.worker_name)
|
||||
? std::string(TR("mining_payout_needs_shielded"))
|
||||
: std::string(TR("toast_pool_payout_invalid"));
|
||||
setMiningError(merr);
|
||||
ui::Notifications::instance().error(merr);
|
||||
if (retargetOnly) stopPoolMining(); // never keep mining on a config that no longer validates
|
||||
return;
|
||||
}
|
||||
@@ -3667,6 +3675,7 @@ void App::postPoolMinerJob(int threads, bool retargetOnly)
|
||||
|
||||
if (cfg.wallet_address.empty()) {
|
||||
DEBUG_LOGF("[ERROR] Pool mining: No wallet address available\n");
|
||||
setMiningError(TR("toast_no_address_generate_z"));
|
||||
ui::Notifications::instance().error(TR("toast_no_address_generate_z"));
|
||||
if (retargetOnly) stopPoolMining();
|
||||
return;
|
||||
@@ -3715,13 +3724,15 @@ void App::postPoolMinerJob(int threads, bool retargetOnly)
|
||||
DEBUG_LOGF("[ERROR] Pool mining: %s\n", err.c_str());
|
||||
// Windows Defender blocking (error 225 = ERROR_VIRUS_INFECTED)
|
||||
if (err.find("error 225") != std::string::npos || err.find("virus") != std::string::npos) {
|
||||
ui::Notifications::instance().error(
|
||||
"Windows Defender blocked xmrig. Add exclusion for %APPDATA%\\ObsidianDragon");
|
||||
setMiningError(TR("toast_av_blocked_xmrig"), /*av=*/true);
|
||||
ui::Notifications::instance().error(TR("toast_av_blocked_xmrig"));
|
||||
#ifdef _WIN32
|
||||
pending_antivirus_dialog_ = true;
|
||||
#endif
|
||||
} else {
|
||||
ui::Notifications::instance().error(std::string(TR("toast_pool_start_failed")) + util::friendlyRpcError(err));
|
||||
const std::string merr = std::string(TR("toast_pool_start_failed")) + util::friendlyRpcError(err);
|
||||
setMiningError(merr);
|
||||
ui::Notifications::instance().error(merr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user