Replace the English string literals in the miner-update dialog + the "Update miner…" mining-tab button/tooltip with TR() keys, and register their English text in i18n.cpp's loadBuiltinEnglish() (the in-code English fallback that non-English locales overlay). Reuses the existing cancel/close/ retry keys. Labeled values use a "%s %s" literal format with a TR'd label (no -Wformat-security risk). Non-English locales fall back to English for the new xmrig_* keys until translations are added to res/lang/*.json. Both variants build; suite passes; hygiene clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
189 lines
7.4 KiB
C++
189 lines
7.4 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
//
|
|
// Modal dialog to download / update the DRG-XMRig miner from the project Gitea, driven by
|
|
// util::XmrigUpdater. States follow the updater: Checking -> UpToDate/UpdateAvailable ->
|
|
// Downloading/Verifying/Extracting -> Done / Failed. Header-only (static inline state), mirroring
|
|
// BootstrapDownloadDialog. The installed release tag is persisted to settings on success.
|
|
|
|
#pragma once
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "../../app.h"
|
|
#include "../../config/settings.h"
|
|
#include "../../resources/embedded_resources.h" // resources::getDaemonDirectory()
|
|
#include "../../util/i18n.h"
|
|
#include "../../util/xmrig_updater.h"
|
|
#include "../material/draw_helpers.h"
|
|
#include "../material/type.h"
|
|
#include "../material/colors.h"
|
|
#include "../theme.h"
|
|
#include "imgui.h"
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
class XmrigDownloadDialog {
|
|
public:
|
|
static void show(App* app) {
|
|
if (!app) return;
|
|
s_app = app;
|
|
s_open = true;
|
|
s_persisted = false;
|
|
s_updater = std::make_unique<util::XmrigUpdater>();
|
|
s_updater->startCheck(app->settings() ? app->settings()->getXmrigVersion() : std::string());
|
|
}
|
|
|
|
static bool isOpen() { return s_open; }
|
|
|
|
static void render() {
|
|
if (!s_open || !s_app || !s_updater) {
|
|
if (!s_open) s_updater.reset(); // closed: drop the updater (dtor joins the worker)
|
|
return;
|
|
}
|
|
using namespace material;
|
|
const float dp = Layout::dpiScale();
|
|
if (BeginOverlayDialog(TR("xmrig_update_title"), &s_open, 480.0f, 0.94f)) {
|
|
const auto p = s_updater->getProgress();
|
|
using St = util::XmrigUpdater::State;
|
|
switch (p.state) {
|
|
case St::Checking: renderChecking(dp, p); break;
|
|
case St::UpToDate:
|
|
case St::UpdateAvailable: renderPrompt(dp, p); break;
|
|
case St::Unavailable: renderUnavailable(dp, p); break;
|
|
case St::Downloading:
|
|
case St::Verifying:
|
|
case St::Extracting: renderProgress(dp, p); break;
|
|
case St::Done: renderDone(dp, p); break;
|
|
case St::Failed: renderFailed(dp, p); break;
|
|
default: break;
|
|
}
|
|
EndOverlayDialog();
|
|
}
|
|
if (!s_open) s_updater.reset();
|
|
}
|
|
|
|
private:
|
|
using Progress = util::XmrigUpdater::Progress;
|
|
using St = util::XmrigUpdater::State;
|
|
|
|
static float fullW() { return ImGui::GetContentRegionAvail().x; }
|
|
|
|
// Install button that refuses (with a note) while the miner is running, re-checked live so a
|
|
// dialog opened before mining started can't replace a now-running binary (TOCTOU guard).
|
|
static void installAction(const char* label) {
|
|
using namespace material;
|
|
if (s_app->isPoolMinerRunning()) {
|
|
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
|
|
TR("xmrig_stop_mining_first"));
|
|
return;
|
|
}
|
|
if (TactileButton(label, ImVec2(fullW(), 0)))
|
|
s_updater->startInstall(resources::getDaemonDirectory());
|
|
}
|
|
|
|
static void renderChecking(float, const Progress&) {
|
|
using namespace material;
|
|
Type().text(TypeStyle::Body2, TR("xmrig_checking"));
|
|
}
|
|
|
|
static void renderUnavailable(float, const Progress& p) {
|
|
using namespace material;
|
|
Type().text(TypeStyle::Subtitle2, TR("xmrig_unavailable_title"));
|
|
ImGui::Spacing();
|
|
ImGui::TextWrapped("%s", p.status_text.empty()
|
|
? TR("xmrig_unavailable_body")
|
|
: p.status_text.c_str());
|
|
ImGui::Spacing();
|
|
if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
|
|
}
|
|
|
|
static void renderPrompt(float, const Progress& p) {
|
|
using namespace material;
|
|
const std::string installed = p.installed_tag.empty() ? "none" : p.installed_tag;
|
|
if (p.state == St::UpdateAvailable) {
|
|
Type().text(TypeStyle::Subtitle2, TR("xmrig_update_available"));
|
|
ImGui::Spacing();
|
|
ImGui::Text("%s %s", TR("xmrig_latest"), p.latest_tag.c_str());
|
|
ImGui::Text("%s %s", TR("xmrig_installed"), installed.c_str());
|
|
ImGui::Spacing();
|
|
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("xmrig_verify_note"));
|
|
ImGui::Spacing();
|
|
installAction(TR("xmrig_download_install"));
|
|
} else {
|
|
Type().text(TypeStyle::Subtitle2, TR("xmrig_up_to_date"));
|
|
ImGui::Spacing();
|
|
ImGui::Text("%s %s", TR("xmrig_installed"), p.latest_tag.c_str());
|
|
ImGui::Spacing();
|
|
installAction(TR("xmrig_reinstall"));
|
|
}
|
|
ImGui::Spacing();
|
|
if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
|
|
}
|
|
|
|
static void renderProgress(float dp, const Progress& p) {
|
|
using namespace material;
|
|
const char* title = p.state == St::Downloading ? TR("xmrig_downloading")
|
|
: p.state == St::Verifying ? TR("xmrig_verifying")
|
|
: TR("xmrig_installing");
|
|
Type().text(TypeStyle::Subtitle2, title);
|
|
ImGui::Spacing();
|
|
const float barH = 8.0f * dp;
|
|
const float barW = fullW();
|
|
const ImVec2 bMin = ImGui::GetCursorScreenPos();
|
|
const ImVec2 bMax(bMin.x + barW, bMin.y + barH);
|
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
|
dl->AddRectFilled(bMin, bMax, IM_COL32(255, 255, 255, 30), 4.0f * dp);
|
|
const float fillW = barW * (p.percent / 100.0f);
|
|
if (fillW > 0)
|
|
dl->AddRectFilled(bMin, ImVec2(bMin.x + fillW, bMax.y), Primary(), 4.0f * dp);
|
|
ImGui::Dummy(ImVec2(0, barH));
|
|
ImGui::Spacing();
|
|
ImGui::Text("%s", p.status_text.c_str());
|
|
ImGui::Spacing();
|
|
// Cancel aborts the in-flight transfer promptly (the curl progress callback returns abort).
|
|
if (TactileButton(TR("cancel"), ImVec2(fullW(), 0))) {
|
|
s_updater->cancel();
|
|
s_open = false;
|
|
}
|
|
}
|
|
|
|
static void renderDone(float, const Progress& p) {
|
|
using namespace material;
|
|
if (!s_persisted) {
|
|
if (s_app->settings() && !p.latest_tag.empty()) {
|
|
s_app->settings()->setXmrigVersion(p.latest_tag);
|
|
s_app->settings()->save();
|
|
}
|
|
s_persisted = true;
|
|
}
|
|
Type().textColored(TypeStyle::Subtitle2, Success(), TR("xmrig_installed_ok"));
|
|
ImGui::Spacing();
|
|
ImGui::Text("%s %s", TR("xmrig_version"), p.latest_tag.c_str());
|
|
ImGui::Spacing();
|
|
if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
|
|
}
|
|
|
|
static void renderFailed(float, const Progress& p) {
|
|
using namespace material;
|
|
Type().textColored(TypeStyle::Subtitle2, Error(), TR("xmrig_update_failed"));
|
|
ImGui::Spacing();
|
|
ImGui::TextWrapped("%s", p.error.empty() ? TR("xmrig_unknown_error") : p.error.c_str());
|
|
ImGui::Spacing();
|
|
installAction(TR("retry"));
|
|
ImGui::Spacing();
|
|
if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
|
|
}
|
|
|
|
static inline bool s_open = false;
|
|
static inline bool s_persisted = false;
|
|
static inline App* s_app = nullptr;
|
|
static inline std::unique_ptr<util::XmrigUpdater> s_updater;
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|