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:
+21
-2
@@ -1233,14 +1233,18 @@ void App::update()
|
|||||||
if (xmrig_manager_ && state_.pool_mining.xmrig_running
|
if (xmrig_manager_ && state_.pool_mining.xmrig_running
|
||||||
&& xmrig_manager_->getState() == daemon::XmrigManager::State::Error) {
|
&& xmrig_manager_->getState() == daemon::XmrigManager::State::Error) {
|
||||||
state_.pool_mining.xmrig_running = false;
|
state_.pool_mining.xmrig_running = false;
|
||||||
|
state_.pool_mining.running_since = 0;
|
||||||
|
state_.pool_mining.last_error.clear();
|
||||||
state_.pool_mining.hashrate_10s = 0.0;
|
state_.pool_mining.hashrate_10s = 0.0;
|
||||||
state_.pool_mining.hashrate_60s = 0.0;
|
state_.pool_mining.hashrate_60s = 0.0;
|
||||||
state_.pool_mining.hashrate_15m = 0.0;
|
state_.pool_mining.hashrate_15m = 0.0;
|
||||||
pool_starting_.store(false, std::memory_order_relaxed);
|
pool_starting_.store(false, std::memory_order_relaxed);
|
||||||
pool_threads_target_.store(0, std::memory_order_relaxed); // that miner is gone
|
pool_threads_target_.store(0, std::memory_order_relaxed); // that miner is gone
|
||||||
const std::string err = xmrig_manager_->getLastError();
|
const std::string err = xmrig_manager_->getLastError();
|
||||||
ui::Notifications::instance().error(err.empty() ? TR("appx_miner_stopped_unexpectedly")
|
const std::string mmsg = err.empty() ? std::string(TR("appx_miner_stopped_unexpectedly"))
|
||||||
: (std::string(TR("appx_miner_stopped_prefix")) + err));
|
: (std::string(TR("appx_miner_stopped_prefix")) + err);
|
||||||
|
setMiningError(mmsg); // persist: a miner that crashed/was killed otherwise just "did nothing"
|
||||||
|
ui::Notifications::instance().error(mmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll xmrig stats every ~2 seconds (use a simple toggle)
|
// Poll xmrig stats every ~2 seconds (use a simple toggle)
|
||||||
@@ -1273,6 +1277,19 @@ void App::update()
|
|||||||
ps.threads_active = xs.threads_active;
|
ps.threads_active = xs.threads_active;
|
||||||
ps.pool_hashrate = xs.pool_hashrate;
|
ps.pool_hashrate = xs.pool_hashrate;
|
||||||
ps.log_lines = xmrig_manager_->getRecentLines(30);
|
ps.log_lines = xmrig_manager_->getRecentLines(30);
|
||||||
|
if (ps.running_since == 0) ps.running_since = std::time(nullptr);
|
||||||
|
// While not yet connected, surface the newest error-looking log line so "Connecting" can flip
|
||||||
|
// to a real "can't reach pool" after a timeout instead of sitting on red forever.
|
||||||
|
ps.last_error.clear();
|
||||||
|
if (!ps.connected) {
|
||||||
|
for (auto it = ps.log_lines.rbegin(); it != ps.log_lines.rend(); ++it) {
|
||||||
|
std::string l = *it;
|
||||||
|
std::transform(l.begin(), l.end(), l.begin(), [](unsigned char c){ return (char)std::tolower(c); });
|
||||||
|
if (l.find("error") != std::string::npos || l.find("refused") != std::string::npos ||
|
||||||
|
l.find("timed out") != std::string::npos || l.find("unreachable") != std::string::npos ||
|
||||||
|
l.find("dns") != std::string::npos) { ps.last_error = *it; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Record hashrate sample for the chart
|
// Record hashrate sample for the chart
|
||||||
ps.hashrate_history.push_back(ps.hashrate_10s);
|
ps.hashrate_history.push_back(ps.hashrate_10s);
|
||||||
@@ -1283,6 +1300,8 @@ void App::update()
|
|||||||
}
|
}
|
||||||
} else if (xmrig_manager_ && !xmrig_manager_->isRunning()) {
|
} else if (xmrig_manager_ && !xmrig_manager_->isRunning()) {
|
||||||
state_.pool_mining.xmrig_running = false;
|
state_.pool_mining.xmrig_running = false;
|
||||||
|
state_.pool_mining.running_since = 0;
|
||||||
|
state_.pool_mining.last_error.clear();
|
||||||
pool_starting_.store(false, std::memory_order_relaxed); // stopped / never came up
|
pool_starting_.store(false, std::memory_order_relaxed); // stopped / never came up
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -389,6 +389,11 @@ public:
|
|||||||
void startMining(int threads);
|
void startMining(int threads);
|
||||||
void stopMining();
|
void stopMining();
|
||||||
bool isMiningToggleInProgress() const { return mining_toggle_in_progress_.load(std::memory_order_relaxed); }
|
bool isMiningToggleInProgress() const { return mining_toggle_in_progress_.load(std::memory_order_relaxed); }
|
||||||
|
// Persistent mining error (last start/stop/pool failure) so a failed miner isn't just a fading toast.
|
||||||
|
const std::string& miningLastError() const { return mining_last_error_; }
|
||||||
|
bool miningErrorIsAntivirus() const { return mining_error_av_; }
|
||||||
|
void clearMiningError() { mining_last_error_.clear(); mining_error_av_ = false; }
|
||||||
|
void showAntivirusHelp() { pending_antivirus_dialog_ = true; } // Windows-only dialog; inert elsewhere
|
||||||
|
|
||||||
// Pool mining (xmrig)
|
// Pool mining (xmrig)
|
||||||
void startPoolMining(int threads);
|
void startPoolMining(int threads);
|
||||||
@@ -1745,6 +1750,10 @@ private:
|
|||||||
|
|
||||||
// Mining toggle guard (prevents concurrent setgenerate calls)
|
// Mining toggle guard (prevents concurrent setgenerate calls)
|
||||||
std::atomic<bool> mining_toggle_in_progress_{false};
|
std::atomic<bool> mining_toggle_in_progress_{false};
|
||||||
|
// Mining error state (main-thread only — every writer is in a MainCb or render).
|
||||||
|
std::string mining_last_error_;
|
||||||
|
bool mining_error_av_ = false;
|
||||||
|
void setMiningError(std::string msg, bool av = false) { mining_last_error_ = std::move(msg); mining_error_av_ = av; }
|
||||||
|
|
||||||
// True from a successful startPoolMining() until the miner is confirmed connected/hashing in the
|
// True from a successful startPoolMining() until the miner is confirmed connected/hashing in the
|
||||||
// poll — drives the "connecting…" → "connected" feedback for pool mining (which has a connect delay).
|
// poll — drives the "connecting…" → "connected" feedback for pool mining (which has a connect delay).
|
||||||
|
|||||||
+19
-8
@@ -3529,6 +3529,7 @@ void App::startMining(int threads)
|
|||||||
const int maxThreads = std::max(1, (int)std::thread::hardware_concurrency());
|
const int maxThreads = std::max(1, (int)std::thread::hardware_concurrency());
|
||||||
threads = std::clamp(threads, 1, maxThreads);
|
threads = std::clamp(threads, 1, maxThreads);
|
||||||
if (mining_toggle_in_progress_.exchange(true)) return; // already in progress
|
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 {
|
worker_->post([this, threads]() -> rpc::RPCWorker::MainCb {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -3548,7 +3549,9 @@ void App::startMining(int threads)
|
|||||||
DEBUG_LOGF("Mining started with %d threads\n", threads);
|
DEBUG_LOGF("Mining started with %d threads\n", threads);
|
||||||
} else {
|
} else {
|
||||||
DEBUG_LOGF("Failed to start mining: %s\n", errMsg.c_str());
|
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 (!supportsSoloMining()) return;
|
||||||
if (!state_.connected || !rpc_ || !worker_) return;
|
if (!state_.connected || !rpc_ || !worker_) return;
|
||||||
if (mining_toggle_in_progress_.exchange(true)) return; // already in progress
|
if (mining_toggle_in_progress_.exchange(true)) return; // already in progress
|
||||||
|
clearMiningError();
|
||||||
|
|
||||||
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
@@ -3580,7 +3584,9 @@ void App::stopMining()
|
|||||||
} else {
|
} else {
|
||||||
// Don't silently leave generate=true as if it worked: tell the user and let the next
|
// 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)
|
// 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).
|
// 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" &&
|
if (!cfg.worker_name.empty() && cfg.worker_name != "x" &&
|
||||||
!util::isValidRecipientAddress(cfg.worker_name)) {
|
!util::isValidRecipientAddress(cfg.worker_name)) {
|
||||||
ui::Notifications::instance().error(util::isTransparentAddress(cfg.worker_name)
|
const std::string merr = util::isTransparentAddress(cfg.worker_name)
|
||||||
? TR("mining_payout_needs_shielded")
|
? std::string(TR("mining_payout_needs_shielded"))
|
||||||
: "Pool payout address is not a valid DragonX address — mining not started.");
|
: 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
|
if (retargetOnly) stopPoolMining(); // never keep mining on a config that no longer validates
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3667,6 +3675,7 @@ void App::postPoolMinerJob(int threads, bool retargetOnly)
|
|||||||
|
|
||||||
if (cfg.wallet_address.empty()) {
|
if (cfg.wallet_address.empty()) {
|
||||||
DEBUG_LOGF("[ERROR] Pool mining: No wallet address available\n");
|
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"));
|
ui::Notifications::instance().error(TR("toast_no_address_generate_z"));
|
||||||
if (retargetOnly) stopPoolMining();
|
if (retargetOnly) stopPoolMining();
|
||||||
return;
|
return;
|
||||||
@@ -3715,13 +3724,15 @@ void App::postPoolMinerJob(int threads, bool retargetOnly)
|
|||||||
DEBUG_LOGF("[ERROR] Pool mining: %s\n", err.c_str());
|
DEBUG_LOGF("[ERROR] Pool mining: %s\n", err.c_str());
|
||||||
// Windows Defender blocking (error 225 = ERROR_VIRUS_INFECTED)
|
// Windows Defender blocking (error 225 = ERROR_VIRUS_INFECTED)
|
||||||
if (err.find("error 225") != std::string::npos || err.find("virus") != std::string::npos) {
|
if (err.find("error 225") != std::string::npos || err.find("virus") != std::string::npos) {
|
||||||
ui::Notifications::instance().error(
|
setMiningError(TR("toast_av_blocked_xmrig"), /*av=*/true);
|
||||||
"Windows Defender blocked xmrig. Add exclusion for %APPDATA%\\ObsidianDragon");
|
ui::Notifications::instance().error(TR("toast_av_blocked_xmrig"));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
pending_antivirus_dialog_ = true;
|
pending_antivirus_dialog_ = true;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -254,6 +254,8 @@ struct PoolMiningState {
|
|||||||
int64_t uptime_sec = 0;
|
int64_t uptime_sec = 0;
|
||||||
double pool_diff = 0;
|
double pool_diff = 0;
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
|
std::time_t running_since = 0; // wall-clock the miner was first seen running this session (0 = not running)
|
||||||
|
std::string last_error; // newest xmrig log line that looks like a connection error ("" = none)
|
||||||
|
|
||||||
// Memory/thread usage (bytes for memory)
|
// Memory/thread usage (bytes for memory)
|
||||||
int64_t memory_used = 0;
|
int64_t memory_used = 0;
|
||||||
|
|||||||
@@ -991,6 +991,30 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
|||||||
|
|
||||||
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
|
||||||
ImGui::Dummy(ImVec2(availWidth, 0));
|
ImGui::Dummy(ImVec2(availWidth, 0));
|
||||||
|
|
||||||
|
// Persistent error caption (a crashed/AV-blocked/failed miner otherwise leaves a button that
|
||||||
|
// "did nothing" once its toast faded). Normal ImGui flow, OUTSIDE the hand-drawn button card.
|
||||||
|
const std::string& merr = app->miningLastError();
|
||||||
|
if (!merr.empty() && !app->isMiningToggleInProgress()) {
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
|
ImGui::PushTextWrapPos(cardMin.x + availWidth);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Error()));
|
||||||
|
ImGui::TextWrapped("%s", merr.c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()));
|
||||||
|
ImGui::TextUnformatted(TR("mining_error_retry_hint"));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (app->miningErrorIsAntivirus()) {
|
||||||
|
ImGui::SameLine(0, Layout::spacingSm());
|
||||||
|
if (ImGui::SmallButton(TR("mining_error_av_help"))) app->showAntivirusHelp();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ImGui::SameLine(0, Layout::spacingSm());
|
||||||
|
if (ImGui::SmallButton(TR("mining_error_dismiss"))) app->clearMiningError();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Dummy(ImVec2(0, gap));
|
ImGui::Dummy(ImVec2(0, gap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,21 @@ static void RenderLeftPoolCard(App* app, const WalletState& state, ImDrawList* d
|
|||||||
else snprintf(uptBuf, sizeof(uptBuf), "%lldh %lldm", (long long)(up / 3600), (long long)((up % 3600) / 60));
|
else snprintf(uptBuf, sizeof(uptBuf), "%lldh %lldm", (long long)(up / 3600), (long long)((up % 3600) / 60));
|
||||||
metricRow(TR("mining_uptime"), uptBuf, OnSurface());
|
metricRow(TR("mining_uptime"), uptBuf, OnSurface());
|
||||||
}
|
}
|
||||||
|
// Connection status — replaces the old transient "Connecting" banner, with a timeout so a bad URL /
|
||||||
|
// blocked port flips from "Connecting" to "Can't reach pool" instead of spinning on red forever.
|
||||||
|
{
|
||||||
|
static constexpr int kPoolConnectTimeoutSec = 30;
|
||||||
|
const bool shares = pm.accepted + pm.rejected > 0;
|
||||||
|
const bool ok = pm.connected || shares || pm.hashrate_10s > 0;
|
||||||
|
const bool failed = pm.xmrig_running && !ok && pm.running_since > 0 &&
|
||||||
|
(std::time(nullptr) - pm.running_since) >= kPoolConnectTimeoutSec;
|
||||||
|
const char* stateText; ImU32 col;
|
||||||
|
if (ok) { stateText = TR("mining_status_connected"); col = Success(); }
|
||||||
|
else if (failed) { stateText = TR("mining_pool_unreachable"); col = Error(); }
|
||||||
|
else if (pm.xmrig_running) { stateText = TR("mining_status_connecting"); col = Warning(); }
|
||||||
|
else { stateText = TR("mining_status_stopped"); col = OnSurfaceDisabled(); }
|
||||||
|
metricRow(TR("mining_status_label"), stateText, col);
|
||||||
|
}
|
||||||
y += gap * 0.5f;
|
y += gap * 0.5f;
|
||||||
|
|
||||||
// The pool list = official pools ∪ user-saved favorites ∪ the current custom pool.
|
// The pool list = official pools ∪ user-saved favorites ∪ the current custom pool.
|
||||||
@@ -397,7 +412,7 @@ void RenderMiningStats(const WalletState& state, const MiningInfo& mining,
|
|||||||
const float mRowH = sub1->LegacySize + 7 * dp;
|
const float mRowH = sub1->LegacySize + 7 * dp;
|
||||||
const float listMinH = (capFont->LegacySize + 10 * dp) * 2.6f; // ~2–3 rows before scroll
|
const float listMinH = (capFont->LegacySize + 10 * dp) * 2.6f; // ~2–3 rows before scroll
|
||||||
float leftContentH = pad * 0.75f + toggleH + gap
|
float leftContentH = pad * 0.75f + toggleH + gap
|
||||||
+ mRowH * 4.0f + gap * 0.5f
|
+ mRowH * 5.0f + gap * 0.5f // 4 metric rows + the connection-status row
|
||||||
+ (ovFont->LegacySize + 4 * dp) + listMinH + pad * 0.75f;
|
+ (ovFont->LegacySize + 4 * dp) + listMinH + pad * 0.75f;
|
||||||
float cardH = std::max(leftContentH, statRowH + chartBudgetH + pad);
|
float cardH = std::max(leftContentH, statRowH + chartBudgetH + pad);
|
||||||
|
|
||||||
|
|||||||
@@ -335,24 +335,9 @@ static void RenderMiningTabContent(App* app)
|
|||||||
isMiningActive, s_pool_mode, availWidth, glassSpec, sHdr, gapOver);
|
isMiningActive, s_pool_mode, availWidth, glassSpec, sHdr, gapOver);
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
// POOL CONNECTION STATUS — inline indicator (pool mode, no log)
|
// (Pool connection status now lives as a persistent, timeout-aware row in RenderLeftPoolCard
|
||||||
// ================================================================
|
// (mining_stats.cpp) — the old transient "Connecting" banner that vanished once logs arrived and
|
||||||
if (s_pool_mode && state.pool_mining.log_lines.empty() && state.pool_mining.xmrig_running) {
|
// sat on red forever on failure was removed.)
|
||||||
ImFont* iconFont = Type().iconSmall();
|
|
||||||
const char* dotIcon = ICON_MD_CIRCLE;
|
|
||||||
ImU32 dotCol = state.pool_mining.connected ? WithAlpha(Success(), 200) : WithAlpha(Error(), 200);
|
|
||||||
const char* statusText = state.pool_mining.connected
|
|
||||||
? (state.pool_mining.pool_url.empty() ? TR("mining_connected") : state.pool_mining.pool_url.c_str())
|
|
||||||
: TR("mining_connecting");
|
|
||||||
|
|
||||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
|
||||||
ImVec2 dotSz = iconFont->CalcTextSizeA(iconFont->LegacySize, 1000.0f, 0.0f, dotIcon);
|
|
||||||
dl->AddText(iconFont, iconFont->LegacySize, pos, dotCol, dotIcon);
|
|
||||||
dl->AddText(capFont, capFont->LegacySize,
|
|
||||||
ImVec2(pos.x + dotSz.x + 4 * dp, pos.y + (dotSz.y - capFont->LegacySize) * 0.5f),
|
|
||||||
OnSurfaceMedium(), statusText);
|
|
||||||
ImGui::Dummy(ImVec2(availWidth, capFont->LegacySize + 4 * dp));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|||||||
@@ -1957,6 +1957,11 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["toast_pool_connecting"] = "Starting pool miner — connecting to the pool…";
|
strings_["toast_pool_connecting"] = "Starting pool miner — connecting to the pool…";
|
||||||
strings_["toast_not_connected_daemon"] = "Not connected to the node.";
|
strings_["toast_not_connected_daemon"] = "Not connected to the node.";
|
||||||
strings_["toast_mining_failed"] = "Couldn't start mining: ";
|
strings_["toast_mining_failed"] = "Couldn't start mining: ";
|
||||||
|
strings_["toast_pool_payout_invalid"] = "Pool payout address is not a valid DragonX address — mining not started.";
|
||||||
|
strings_["toast_av_blocked_xmrig"] = "Windows Defender blocked the miner. Add an exclusion for %APPDATA%\\ObsidianDragon.";
|
||||||
|
strings_["mining_error_retry_hint"] = "Press Mine to try again.";
|
||||||
|
strings_["mining_error_av_help"] = "How to fix";
|
||||||
|
strings_["mining_error_dismiss"] = "Dismiss";
|
||||||
strings_["toast_stop_mining_failed"] = "Couldn't stop mining: ";
|
strings_["toast_stop_mining_failed"] = "Couldn't stop mining: ";
|
||||||
strings_["toast_pool_start_failed"] = "Couldn't start the pool miner: ";
|
strings_["toast_pool_start_failed"] = "Couldn't start the pool miner: ";
|
||||||
strings_["toast_ban_failed"] = "Couldn't ban the peer: ";
|
strings_["toast_ban_failed"] = "Couldn't ban the peer: ";
|
||||||
@@ -2258,6 +2263,11 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["mining_click_copy_difficulty"] = "Click to copy difficulty";
|
strings_["mining_click_copy_difficulty"] = "Click to copy difficulty";
|
||||||
strings_["mining_connected"] = "Connected";
|
strings_["mining_connected"] = "Connected";
|
||||||
strings_["mining_connecting"] = "Connecting...";
|
strings_["mining_connecting"] = "Connecting...";
|
||||||
|
strings_["mining_status_label"] = "Status";
|
||||||
|
strings_["mining_status_connected"] = "Connected";
|
||||||
|
strings_["mining_status_connecting"] = "Connecting…";
|
||||||
|
strings_["mining_status_stopped"] = "Stopped";
|
||||||
|
strings_["mining_pool_unreachable"] = "Can't reach pool — check the URL";
|
||||||
strings_["mining_difficulty_copied"] = "Difficulty copied";
|
strings_["mining_difficulty_copied"] = "Difficulty copied";
|
||||||
strings_["mining_est_block"] = "Est. Block";
|
strings_["mining_est_block"] = "Est. Block";
|
||||||
strings_["mining_est_daily"] = "Est. Daily";
|
strings_["mining_est_daily"] = "Est. Daily";
|
||||||
|
|||||||
Reference in New Issue
Block a user