diff --git a/src/ui/windows/mining_controls.cpp b/src/ui/windows/mining_controls.cpp index f358881..4831a99 100644 --- a/src/ui/windows/mining_controls.cpp +++ b/src/ui/windows/mining_controls.cpp @@ -579,6 +579,9 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo& std::string curVer = s_live_miner_ver; if (curVer.empty()) curVer = app->poolMiningInstalledVersion(); if (curVer.empty()) curVer = app->settings()->getXmrigVersion(); + // No miner installed at all → pool mining can't run. Make this a prominent + // "Download miner" call-to-action instead of a subtle grey "Update" pill. + const bool minerMissing = curVer.empty(); // Version state: subtle GREEN + "xmrig releases" when up to date, subtle ORANGE + // "Update " when an update is available, neutral when either version is unknown. auto verNum = [](std::string s) -> std::vector { @@ -609,7 +612,9 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo& const ImU32 subtleOrange = IM_COL32(214, 158, 74, 255); char xbtn[64]; - if (upToDate) + if (minerMissing) + snprintf(xbtn, sizeof(xbtn), "%s", TR("xmrig_download_miner")); + else if (upToDate) snprintf(xbtn, sizeof(xbtn), "%s", TR("xmrig_releases")); else if (!s_xmrig_latest_tag.empty()) snprintf(xbtn, sizeof(xbtn), "%s %s", TR("xmrig_update_short"), s_xmrig_latest_tag.c_str()); @@ -629,18 +634,21 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo& bool xhov = ImGui::IsItemHovered(); bool xclk = ImGui::IsItemClicked(); ImU32 xpill = minerBusy ? WithAlpha(OnSurface(), 12) - : (xhov ? StateHover() : WithAlpha(OnSurface(), 28)); + : (minerMissing ? WithAlpha(Primary(), xhov ? 70 : 45) + : (xhov ? StateHover() : WithAlpha(OnSurface(), 28))); dl->AddRectFilled(ImVec2(xbtnX, xbtnY), ImVec2(xbtnX + xbtnW, xbtnY + xbtnH), xpill, xbtnH * 0.3f); dl->AddText(capFont, capFont->LegacySize, ImVec2(xbtnX + xpadX, xbtnY + (xbtnH - xlblSz.y) * 0.5f), minerBusy ? OnSurfaceDisabled() - : (upToDate ? subtleGreen : outdated ? subtleOrange : OnSurfaceMedium()), + : (minerMissing ? Primary() + : (upToDate ? subtleGreen : outdated ? subtleOrange : OnSurfaceMedium())), xbtn); if (xhov) { ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); material::Tooltip("%s", minerBusy ? TR("xmrig_stop_mining_first") - : TR("xmrig_update_title")); + : (minerMissing ? TR("xmrig_download_miner_tip") + : TR("xmrig_update_title"))); } if (xclk && !minerBusy) XmrigDownloadDialog::show(app); diff --git a/src/ui/windows/mining_mode_toggle.cpp b/src/ui/windows/mining_mode_toggle.cpp index d54c8d2..a054bf9 100644 --- a/src/ui/windows/mining_mode_toggle.cpp +++ b/src/ui/windows/mining_mode_toggle.cpp @@ -139,6 +139,8 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo if (soloMiningAvailable && poolHov && !soloMiningActive) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); if (poolHov && soloMiningActive && !s_pool_mode) { material::Tooltip("%s", TR("mining_stop_solo_for_pool")); + } else if (poolHov && !soloMiningActive) { + material::Tooltip("%s", TR("mining_pool_tip")); } ImGui::SetCursorScreenPos(ImVec2(tMin.x, tMax.y)); @@ -439,21 +441,51 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo } } - // --- Worker: Bookmark button --- + // --- Worker: Generate (when empty) / Bookmark button --- + // A new wallet has no z-address, and a pool payout MUST be shielded. When the field is empty + // show a one-click "generate a shielded address" button in place of the (useless-when-empty) + // bookmark, instead of only a hover hint pointing at the Receive tab. ImGui::SameLine(0, 0); { std::string currentWorker(s_pool_worker); - bool alreadySaved = miningValueAlreadySaved(app->settings()->getSavedPoolWorkers(), currentWorker); - material::IconButtonStyle st; - st.color = alreadySaved ? Primary() : OnSurfaceMedium(); - st.hoverBg = StateHover(); - st.bgRounding = 4.0f * dp; - st.tooltip = alreadySaved ? TR("mining_already_saved") : TR("mining_save_payout_address"); - if (material::IconButton("##SaveWorkerAddr", alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER, - Type().iconSmall(), ImVec2(iconBtnW, inputFrameH2), st) - && !currentWorker.empty() && currentWorker != "x" && !alreadySaved) { - app->settings()->addSavedPoolWorker(currentWorker); - app->settings()->save(); + const bool noAddr = currentWorker.empty() || currentWorker == "x"; + if (noAddr) { + material::IconButtonStyle st; + st.color = Primary(); + st.hoverBg = StateHover(); + st.bgRounding = 4.0f * dp; + st.tooltip = TR("mining_generate_z_address"); + if (material::IconButton("##GenZAddr", ICON_MD_ADD, Type().iconSmall(), + ImVec2(iconBtnW, inputFrameH2), st)) { + if (!app->isLiteBuild() && !app->isConnected()) { + ui::Notifications::instance().warning(TR("toast_not_connected_daemon")); + } else { + char* wp = &s_pool_worker[0]; const size_t wn = sizeof(s_pool_worker); + app->createNewZAddress([app, wp, wn](const std::string& addr) { + if (addr.empty()) { + ui::Notifications::instance().error(TR("mining_generate_z_failed")); + return; + } + snprintf(wp, wn, "%s", addr.c_str()); // wp → the static s_pool_worker storage + app->settings()->setPoolWorker(addr); + app->settings()->save(); + ui::Notifications::instance().success(TR("mining_generate_z_ok")); + }); + } + } + } else { + bool alreadySaved = miningValueAlreadySaved(app->settings()->getSavedPoolWorkers(), currentWorker); + material::IconButtonStyle st; + st.color = alreadySaved ? Primary() : OnSurfaceMedium(); + st.hoverBg = StateHover(); + st.bgRounding = 4.0f * dp; + st.tooltip = alreadySaved ? TR("mining_already_saved") : TR("mining_save_payout_address"); + if (material::IconButton("##SaveWorkerAddr", alreadySaved ? ICON_MD_BOOKMARK : ICON_MD_BOOKMARK_BORDER, + Type().iconSmall(), ImVec2(iconBtnW, inputFrameH2), st) + && !alreadySaved) { + app->settings()->addSavedPoolWorker(currentWorker); + app->settings()->save(); + } } } diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 56cf590..ab39edc 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -2289,6 +2289,9 @@ void I18n::loadBuiltinEnglish() strings_["validate_mining_type"] = "Mining address (transparent)"; strings_["mining_est_daily_pool_sub"] = "rough solo-equivalent, before pool fee"; strings_["mining_generate_z_address_hint"] = "Generate a Z address in the Receive tab to use as your payout address"; + strings_["mining_generate_z_address"] = "Generate a shielded payout address"; + strings_["mining_generate_z_ok"] = "Created a shielded payout address."; + strings_["mining_generate_z_failed"] = "Couldn't create a shielded address — try the Receive tab."; strings_["mining_pool_needs_payout_tooltip"] = "Enter a payout address first (generate a Z address)"; strings_["mining_pool"] = "Pool"; strings_["mining_payout_foreign"] = "⚠ This payout address isn't in your current wallet — mined rewards would go to a different wallet. Update it if you switched wallets."; @@ -2328,6 +2331,8 @@ void I18n::loadBuiltinEnglish() strings_["mining_starting_tooltip"] = "Miner is starting..."; strings_["mining_stop"] = "Stop"; strings_["mining_stop_solo_for_pool"] = "Stop solo mining before starting pool mining"; + strings_["mining_solo_tip"] = "Solo: your node mines on its own. No pool fees, but a small CPU may wait a long time between rewards."; + strings_["mining_pool_tip"] = "Pool: team up with other miners for smaller, more frequent payouts. Needs the DRG-XMRig miner and a shielded payout address."; strings_["mining_stop_solo_for_pool_settings"] = "Stop solo mining to change pool settings"; strings_["mining_stopping"] = "Stopping..."; strings_["mining_stopping_tooltip"] = "Miner is stopping..."; @@ -2339,6 +2344,8 @@ void I18n::loadBuiltinEnglish() // --- Miner (xmrig) updater --- strings_["xmrig_update_button"] = "Update miner…"; + strings_["xmrig_download_miner"] = "Download miner"; + strings_["xmrig_download_miner_tip"] = "Pool mining needs the DRG-XMRig miner. Click to download and install it."; strings_["xmrig_update_short"] = "Update"; strings_["xmrig_releases"] = "xmrig releases"; strings_["xmrig_current"] = "Current:";