Fixes the cut-off/clipping bugs from the layout audit (all visible at the default
1280/1024 window sizes):
- Receive "Recent Received" rows: the amount collided with the relative-time
("+15.7500 DRGX14 days ago") and the type label touched the address at narrow
widths. Use the shared short time format (formatTimeAgoShort, matching Overview),
chain the amount's right edge off the measured time width, and start the address
after the measured type-label width — so neither pair can collide.
- Daemon & xmrig updater verify-note: drawn unwrapped and clipped at the card's right
edge; wrap it (PushTextWrapPos) within the already-reserved height.
- Request Payment: the three footer buttons shared one fixed width (clipping "Copy
Full Address"); size each to its own label. The Payment URI overflowed a plain
field; render it in a bordered read-only box (bounded, un-chunked).
- Console: the filter input shrank below its own placeholder (gone entirely at 1024);
give it a min width >= the placeholder and drop the "N lines" count when the row
can't fit both.
- Address-label "Choose Icon" grid: had NoScrollbar hiding most of the catalog with
no cue; give it a real scrollbar.
- Overview "Recent Transactions": drop the 4th row at 1024 (it clipped off-screen) by
capping to rows that fully fit the reserved height.
- Sidebar: reserve the unread-badge width in the nav-label centering so History/Chat
labels no longer collide with their badge.
Verified at 1024 and 1280 across full-node + Lite + Windows (ctest green) and an
adversarial diff review (clean). Skipped the legacy settings_window overlay footer
(dead code / removal candidate).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
442 lines
21 KiB
C++
442 lines
21 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. Two-pane layout (mirrors the node updater / Manage-Portfolio editor): the list
|
|
// of miner versions on the LEFT, the selected version's detail + install action on the RIGHT.
|
|
// States: Listing -> ReleaseList -> Downloading/Verifying/Extracting -> Done / Failed. Header-only.
|
|
// The installed release tag is persisted to settings on success. Installs are refused while the
|
|
// miner is running (you can't replace a running binary).
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#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/colors.h"
|
|
#include "../material/draw_helpers.h"
|
|
#include "../material/markdown.h"
|
|
#include "../material/type.h"
|
|
#include "../theme.h"
|
|
#include "release_list_view.h"
|
|
#include "imgui.h"
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
class XmrigDownloadDialog {
|
|
public:
|
|
using Progress = util::XmrigUpdater::Progress;
|
|
using St = util::XmrigUpdater::State;
|
|
|
|
static void show(App* app) {
|
|
if (!app) return;
|
|
s_app = app;
|
|
s_open = true;
|
|
s_persisted = false;
|
|
s_sel = 0;
|
|
s_sweep = false;
|
|
s_installed_tag = app->settings() ? app->settings()->getXmrigVersion() : std::string();
|
|
s_rows.clear();
|
|
s_releases.clear();
|
|
s_updater = std::make_unique<util::XmrigUpdater>();
|
|
s_updater->startListReleases(); // fetch every version up front for the two-pane picker
|
|
}
|
|
|
|
static bool isOpen() { return s_open; }
|
|
|
|
// Sweep-only: seed fake releases + a forced state so the offline UI sweep can render the modal.
|
|
static void sweepSeed(App* app, std::vector<util::XmrigRelease> releases, St state,
|
|
const std::string& installedTag) {
|
|
s_app = app;
|
|
s_open = true;
|
|
s_persisted = false;
|
|
s_installed_tag = installedTag;
|
|
s_sel = 0;
|
|
s_sweep = true;
|
|
s_releases = std::move(releases);
|
|
s_rows.clear();
|
|
s_updater.reset();
|
|
s_sweepProgress = Progress{};
|
|
s_sweepProgress.state = state;
|
|
if (!s_releases.empty()) s_sweepProgress.latest_tag = s_releases.front().tag;
|
|
s_sweepProgress.installed_tag = installedTag;
|
|
s_sweepProgress.percent = 62.0f;
|
|
s_sweepProgress.status_text = "drg-xmrig-linux-x64.zip";
|
|
}
|
|
static void sweepClose() { s_open = false; s_sweep = false; s_releases.clear(); s_rows.clear(); }
|
|
|
|
static void render() {
|
|
if (!s_open || !s_app) {
|
|
if (!s_open) s_updater.reset();
|
|
return;
|
|
}
|
|
using namespace material;
|
|
const float dp = Layout::dpiScale();
|
|
const Progress p = s_sweep ? s_sweepProgress
|
|
: (s_updater ? s_updater->getProgress() : Progress{});
|
|
const bool active = (p.state == St::Downloading || p.state == St::Verifying || p.state == St::Extracting);
|
|
OverlayDialogSpec ov;
|
|
ov.title = TR("xmrig_update_title");
|
|
ov.p_open = active ? nullptr : &s_open;
|
|
ov.style = OverlayStyle::BlurFloat;
|
|
ov.cardWidth = 880.0f;
|
|
ov.cardHeight = std::min(760.0f, ImGui::GetMainViewport()->Size.y * 0.84f / dp);
|
|
ov.idSuffix = "xmrigupd";
|
|
if (BeginOverlayDialog(ov)) {
|
|
switch (p.state) {
|
|
case St::Idle:
|
|
case St::Checking:
|
|
case St::UpToDate:
|
|
case St::UpdateAvailable:
|
|
case St::Listing: renderCentered(TR("xmrig_loading_releases")); break;
|
|
case St::Unavailable: renderUnavailable(p); break;
|
|
case St::Failed:
|
|
if (s_releases.empty()) { renderListFailed(p); break; }
|
|
renderTwoPane(dp, p, active); break;
|
|
default: renderTwoPane(dp, p, active); break;
|
|
}
|
|
EndOverlayDialog();
|
|
}
|
|
if (!s_open) { s_updater.reset(); s_sweep = false; }
|
|
}
|
|
|
|
private:
|
|
static float fullW() { return ImGui::GetContentRegionAvail().x; }
|
|
|
|
static void renderCentered(const char* msg) {
|
|
using namespace material;
|
|
ImVec2 av = ImGui::GetContentRegionAvail();
|
|
ImFont* f = Type().body2();
|
|
ImVec2 ts = f->CalcTextSizeA(f->LegacySize, FLT_MAX, 0, msg);
|
|
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + std::max(0.0f, (av.x - ts.x) * 0.5f),
|
|
ImGui::GetCursorPosY() + av.y * 0.45f));
|
|
Type().text(TypeStyle::Body2, msg);
|
|
}
|
|
|
|
static void renderUnavailable(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(std::min(fullW(), 200.0f * Layout::dpiScale()), 0)))
|
|
s_open = false;
|
|
}
|
|
|
|
static void renderListFailed(const Progress& p) {
|
|
using namespace material;
|
|
const float bw = std::min(fullW(), 220.0f * Layout::dpiScale());
|
|
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();
|
|
if (TactileButton(TR("retry"), ImVec2(bw, 0)) && s_updater) {
|
|
s_releases.clear();
|
|
s_rows.clear();
|
|
s_updater->startListReleases();
|
|
}
|
|
ImGui::Spacing();
|
|
if (TactileButton(TR("close"), ImVec2(bw, 0))) s_open = false;
|
|
}
|
|
|
|
static void buildRows() {
|
|
if (!s_rows.empty() || s_releases.empty()) return;
|
|
const std::string token = util::currentXmrigPlatformToken();
|
|
s_rows.reserve(s_releases.size());
|
|
for (const auto& r : s_releases) {
|
|
ReleaseRow row;
|
|
row.tag = r.tag;
|
|
row.title = r.name;
|
|
row.date = r.publishedAt.size() >= 10 ? r.publishedAt.substr(0, 10) : r.publishedAt;
|
|
row.prerelease = r.prerelease;
|
|
row.hasAsset = util::selectXmrigAsset(r, token) >= 0;
|
|
row.installed = !s_installed_tag.empty() && r.tag == s_installed_tag;
|
|
s_rows.push_back(std::move(row));
|
|
}
|
|
}
|
|
|
|
static std::string cleanNotes(const std::string& body) {
|
|
std::istringstream ss(body);
|
|
std::string line, out;
|
|
while (std::getline(ss, line)) {
|
|
size_t a = line.find_first_not_of(" \t");
|
|
std::string lt = (a == std::string::npos) ? std::string() : line.substr(a);
|
|
std::string low = lt;
|
|
std::transform(low.begin(), low.end(), low.begin(),
|
|
[](unsigned char c) { return (char)std::tolower(c); });
|
|
if (!lt.empty() && lt[0] == '|') break;
|
|
if (low.rfind("## checksum", 0) == 0 || low.rfind("#### checksum", 0) == 0 ||
|
|
low.rfind("checksums", 0) == 0 || low.rfind("### sha", 0) == 0) break;
|
|
out += line;
|
|
out += '\n';
|
|
}
|
|
while (!out.empty() && (out.back() == '\n' || out.back() == '\r' ||
|
|
out.back() == ' ' || out.back() == '\t')) out.pop_back();
|
|
return out;
|
|
}
|
|
|
|
static void startInstallSelected() {
|
|
if (!s_updater || s_app->isPoolMinerRunning()) return; // can't replace a running miner
|
|
if (s_sel < 0 || s_sel >= (int)s_releases.size()) return;
|
|
const util::XmrigRelease rel = s_releases[s_sel];
|
|
s_updater->startInstallRelease(resources::getDaemonDirectory(), rel);
|
|
}
|
|
|
|
// ================= Two-pane: version list (left) + detail/progress (right) =================
|
|
static void renderTwoPane(float dp, const Progress& p, bool active) {
|
|
using namespace material;
|
|
if (!s_sweep && s_releases.empty() && s_updater) s_releases = s_updater->getReleases();
|
|
buildRows();
|
|
if (s_sel >= (int)s_rows.size()) s_sel = s_rows.empty() ? 0 : (int)s_rows.size() - 1;
|
|
|
|
ImFont* bodyF = Type().body2();
|
|
ImFont* capF = Type().caption();
|
|
|
|
const bool showFooter = (p.state == St::ReleaseList);
|
|
const float footerH = showFooter
|
|
? (ImGui::GetFrameHeight() * 1.25f + ImGui::GetStyle().ItemSpacing.y * 2.0f + Layout::spacingSm())
|
|
: 0.0f;
|
|
const float bodyH = std::max(120.0f * dp, ImGui::GetContentRegionAvail().y - footerH);
|
|
const float contentW = ImGui::GetContentRegionAvail().x;
|
|
const float gap = Layout::spacingLg();
|
|
const float masterW = std::min(std::max(contentW * 0.34f, 240.0f * dp), 340.0f * dp);
|
|
const float detailW = contentW - masterW - gap;
|
|
|
|
// ---- LEFT: borderless list of tactile version cards ----
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
|
ImGui::BeginChild("##xmrigVersions", ImVec2(masterW, bodyH), false);
|
|
{
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, Layout::spacingSm()));
|
|
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0, 0, 0, 0));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0, 0, 0, 0));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0, 0, 0, 0));
|
|
ImDrawList* mdl = ImGui::GetWindowDrawList();
|
|
ImFont* icoF = Type().iconSmall();
|
|
const float rowH = Layout::spacingMd() * 2.0f + bodyF->LegacySize + Layout::spacingXs() + capF->LegacySize;
|
|
const float rowW = ImGui::GetContentRegionAvail().x;
|
|
const float round = 8.0f * dp;
|
|
for (int i = 0; i < (int)s_rows.size(); ++i) {
|
|
const ReleaseRow& r = s_rows[i];
|
|
ImGui::PushID(i);
|
|
bool sel = (s_sel == i);
|
|
ImVec2 rmn = ImGui::GetCursorScreenPos();
|
|
if (ImGui::Selectable("##ver", sel, ImGuiSelectableFlags_SpanAvailWidth, ImVec2(0, rowH))) {
|
|
if (!active) s_sel = i;
|
|
}
|
|
bool hov = ImGui::IsItemHovered();
|
|
bool held = ImGui::IsItemActive();
|
|
ImVec2 rmx(rmn.x + rowW, rmn.y + rowH);
|
|
ImU32 fill = sel ? WithAlpha(Primary(), held ? 55 : 42)
|
|
: held ? WithAlpha(OnSurface(), 34)
|
|
: hov ? WithAlpha(OnSurface(), 24)
|
|
: WithAlpha(OnSurface(), 12);
|
|
mdl->AddRectFilled(rmn, rmx, fill, round);
|
|
if (sel) mdl->AddRect(rmn, rmx, WithAlpha(Primary(), 150), round, 0, 1.6f * dp);
|
|
if (hov || held) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
|
const char* ico = nullptr; ImU32 statusC = 0; const char* tt = nullptr; const char* lbl = nullptr;
|
|
if (r.installed) { ico = ICON_MD_CHECK_CIRCLE; statusC = Success(); tt = TR("xmrig_active_tt"); lbl = TR("upd_installed_badge"); }
|
|
else if (r.prerelease) { ico = ICON_MD_SCIENCE; statusC = Warning(); tt = TR("xmrig_prerelease_tt"); lbl = TR("upd_prerelease"); }
|
|
else if (i == 0) { ico = ICON_MD_NEW_RELEASES; statusC = Primary(); tt = TR("xmrig_latest_tt"); lbl = TR("xmrig_latest_badge"); }
|
|
float rightLimit = rmx.x - Layout::spacingMd();
|
|
if (ico) {
|
|
float cs = bodyF->LegacySize * 1.3f;
|
|
ImVec2 cp(rmx.x - Layout::spacingMd() - cs, (rmn.y + rmx.y) * 0.5f - cs * 0.5f);
|
|
mdl->AddText(icoF, cs, cp, statusC, ico);
|
|
if (tt && hov && ImGui::IsMouseHoveringRect(cp, ImVec2(cp.x + cs, cp.y + cs)))
|
|
Tooltip("%s", tt);
|
|
rightLimit = cp.x - Layout::spacingSm();
|
|
}
|
|
float x = rmn.x + Layout::spacingMd();
|
|
float tagY = rmn.y + Layout::spacingMd();
|
|
mdl->AddText(bodyF, bodyF->LegacySize, ImVec2(x, tagY),
|
|
r.hasAsset ? OnSurface() : OnSurfaceDisabled(), r.tag.c_str());
|
|
if (lbl) {
|
|
float bx = x + bodyF->CalcTextSizeA(bodyF->LegacySize, FLT_MAX, 0, r.tag.c_str()).x + Layout::spacingSm();
|
|
float by = tagY + (bodyF->LegacySize - capF->LegacySize) * 0.5f;
|
|
if (bx + capF->CalcTextSizeA(capF->LegacySize, FLT_MAX, 0, lbl).x <= rightLimit)
|
|
mdl->AddText(capF, capF->LegacySize, ImVec2(bx, by), statusC, lbl);
|
|
}
|
|
if (!r.date.empty())
|
|
mdl->AddText(capF, capF->LegacySize,
|
|
ImVec2(x, rmx.y - Layout::spacingMd() - capF->LegacySize),
|
|
OnSurfaceMedium(), r.date.c_str());
|
|
ImGui::PopID();
|
|
}
|
|
ImGui::PopStyleColor(3);
|
|
ImGui::PopStyleVar();
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::PopStyleVar();
|
|
|
|
ImGui::SameLine(0.0f, gap);
|
|
|
|
// ---- RIGHT: detail / progress / done / failed for the selected version ----
|
|
ImGui::BeginChild("##xmrigDetail", ImVec2(detailW, bodyH), false);
|
|
switch (p.state) {
|
|
case St::ReleaseList: renderDetail(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;
|
|
}
|
|
ImGui::EndChild();
|
|
|
|
if (showFooter) {
|
|
ImGui::Spacing();
|
|
const float bw = 170.0f * dp, bh = ImGui::GetFrameHeight() * 1.25f;
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + std::max(0.0f, ImGui::GetContentRegionAvail().x - bw));
|
|
if (TactileButton(TR("close"), ImVec2(bw, bh))) s_open = false;
|
|
}
|
|
}
|
|
|
|
static void renderDetail(float dp, const Progress&) {
|
|
using namespace material;
|
|
if (s_sel < 0 || s_sel >= (int)s_releases.size() || s_sel >= (int)s_rows.size()) return;
|
|
const util::XmrigRelease& rel = s_releases[s_sel];
|
|
const ReleaseRow& row = s_rows[s_sel];
|
|
const bool haveInstalled = !s_installed_tag.empty();
|
|
const bool downgrade = !row.installed && s_sel != 0 && haveInstalled;
|
|
const bool mining = s_app->isPoolMinerRunning();
|
|
|
|
const char* noteStr = mining ? TR("xmrig_stop_mining_first")
|
|
: !row.hasAsset ? TR("upd_no_build_platform")
|
|
: downgrade ? TR("xmrig_downgrade_note")
|
|
: TR("xmrig_verify_note");
|
|
const char* label = row.installed ? TR("xmrig_reinstall")
|
|
: (s_sel == 0) ? TR("xmrig_download_install")
|
|
: TR("xmrig_install_this");
|
|
|
|
const float btnH = ImGui::GetFrameHeight() * 1.15f;
|
|
const float noteH = ImGui::CalcTextSize(noteStr, nullptr, false, ImGui::GetContentRegionAvail().x).y;
|
|
const float reserve = noteH + btnH + Layout::spacingMd() * 3.0f;
|
|
const float infoH = std::max(120.0f * dp, ImGui::GetContentRegionAvail().y - reserve);
|
|
|
|
// ---- Info card: ONE rounded Material surface (version header + release notes markdown) ----
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, WithAlpha(OnSurface(), 22));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f * dp);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(Layout::spacingLg(), Layout::spacingLg()));
|
|
ImGui::BeginChild("##xmrigInfo", ImVec2(0, infoH), ImGuiChildFlags_AlwaysUseWindowPadding);
|
|
{
|
|
Type().text(TypeStyle::H6, rel.tag.c_str());
|
|
if (row.installed)
|
|
Type().textColored(TypeStyle::Subtitle2, Success(), TR("xmrig_status_current"));
|
|
else if (s_sel == 0)
|
|
Type().textColored(TypeStyle::Subtitle2, Primary(),
|
|
haveInstalled ? TR("xmrig_update_available") : TR("xmrig_status_latest"));
|
|
else if (haveInstalled)
|
|
Type().textColored(TypeStyle::Subtitle2, Warning(), TR("xmrig_status_older"));
|
|
else
|
|
Type().text(TypeStyle::Subtitle2, rel.name.empty() ? rel.tag.c_str() : rel.name.c_str());
|
|
|
|
const std::string installed = s_installed_tag.empty() ? std::string(TR("xmrig_none"))
|
|
: s_installed_tag;
|
|
ImGui::Spacing();
|
|
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
|
|
(std::string(TR("xmrig_installed")) + " " + installed).c_str());
|
|
if (!row.date.empty())
|
|
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
|
|
(std::string(TR("xmrig_released")) + " " + row.date).c_str());
|
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
|
|
|
const std::string notes = cleanNotes(rel.body);
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
|
|
ImGui::BeginChild("##xmrigNotes", ImVec2(0, std::max(0.0f, ImGui::GetContentRegionAvail().y)), false);
|
|
if (notes.empty())
|
|
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("xmrig_no_notes"));
|
|
else
|
|
RenderMarkdown(notes);
|
|
ImGui::EndChild();
|
|
ImGui::PopStyleColor();
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::PopStyleVar(2);
|
|
ImGui::PopStyleColor();
|
|
|
|
// ---- Below the info card: verify / stop-mining note + install button (centered, text-fit) ----
|
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
|
ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x);
|
|
Type().textColored(TypeStyle::Caption, (mining || downgrade) ? Warning() : OnSurfaceMedium(), noteStr);
|
|
ImGui::PopTextWrapPos();
|
|
ImGui::Spacing();
|
|
const float bw = ImGui::CalcTextSize(label).x + ImGui::GetStyle().FramePadding.x * 2.0f + Layout::spacingLg();
|
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + std::max(0.0f, (ImGui::GetContentRegionAvail().x - bw) * 0.5f));
|
|
ImGui::BeginDisabled(!row.hasAsset || mining);
|
|
if (TactileButton(label, ImVec2(bw, btnH))) startInstallSelected();
|
|
ImGui::EndDisabled();
|
|
}
|
|
|
|
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();
|
|
material::ProgressBar(fullW(), 8.0f * dp, p.percent / 100.0f);
|
|
ImGui::Spacing();
|
|
ImGui::TextWrapped("%s", p.status_text.c_str());
|
|
ImGui::Spacing();
|
|
if (TactileButton(TR("cancel"), ImVec2(fullW(), 0))) {
|
|
if (s_updater) 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();
|
|
ImGui::BeginDisabled(s_app->isPoolMinerRunning());
|
|
if (TactileButton(TR("retry"), ImVec2(fullW(), 0))) startInstallSelected();
|
|
ImGui::EndDisabled();
|
|
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 bool s_sweep = false;
|
|
static inline int s_sel = 0;
|
|
static inline std::string s_installed_tag;
|
|
static inline std::vector<ReleaseRow> s_rows; // built once per listing (UI rows)
|
|
static inline std::vector<util::XmrigRelease> s_releases; // matching releases (install by index)
|
|
static inline Progress s_sweepProgress;
|
|
static inline App* s_app = nullptr;
|
|
static inline std::unique_ptr<util::XmrigUpdater> s_updater;
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|