feat(mining): persistent payout error, solo-address explainer, i18n cleanup

- The pool payout-address rule is no longer hover-only: an invalid payout
  address now outlines the field in red and shows a short reason below it (a row
  is reserved so it doesn't overlap the card beneath). The longer tooltip stays.
- The solo "Mining Addr" row gains a hover explainer and a "None yet" empty-state
  (with a tooltip pointing to Receive → New mining address) instead of a blank.
- Replaced hardcoded English in the earnings/stats cards ("(%d txn)", uptime
  "N/A"/"%lldm %llds"/"%lldh %lldm") with TR() keys.

Also carries the chat-export confirm strings used by the next commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-18 23:04:58 -05:00
co-authored by Claude Opus 4.8
parent 3f127f9b62
commit dcb8e6eb25
4 changed files with 51 additions and 7 deletions
+23 -1
View File
@@ -146,6 +146,8 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
ImGui::SetCursorScreenPos(ImVec2(tMin.x, tMax.y));
ImGui::Dummy(ImVec2(totalW, 0));
bool payoutBadThisFrame = false; // set when the inline payout error is drawn; reserves a row below
// Pool URL + Worker inputs inline next to toggle (pool mode only)
if (s_pool_mode && soloMiningActive) {
// Solo mining is active — show disabled message instead of inputs
@@ -427,6 +429,24 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
}
}
// Persistent inline payout error — a hover-only tooltip is missed by anyone who doesn't hover,
// so outline the field in red and draw a short reason below it (draw-list only; no layout calls
// here, the cursor is hand-positioned). The field height is reserved at the end of the function.
{
const ImVec2 wMin = ImGui::GetItemRectMin();
const ImVec2 wMax = ImGui::GetItemRectMax();
const std::string wk(s_pool_worker);
if (!wk.empty() && wk != "x" && !util::isValidRecipientAddress(wk)) {
payoutBadThisFrame = true;
dl->AddRect(wMin, wMax, Error(), 4.0f * dp, 0, 1.5f * dp);
const char* msg = util::isTransparentAddress(wk)
? TR("mining_payout_needs_shielded_short") : TR("mining_payout_invalid_short");
std::string shown = material::TruncateToWidth(msg, capFont, capFont->LegacySize, wrkGroupW);
dl->AddText(capFont, capFont->LegacySize, ImVec2(wMin.x, wMax.y + 1.0f * dp),
ReadableError(), shown.c_str());
}
}
// --- Worker: Dropdown arrow button ---
ImGui::SameLine(0, 0);
{
@@ -642,7 +662,9 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
// Ensure cursor Y is at toggle bottom regardless of pool input widgets,
// so the cards below stay at the same position in both solo and pool modes.
ImGui::SetCursorScreenPos(ImVec2(tMin.x, tMax.y));
ImGui::Dummy(ImVec2(0, gap * 0.5f));
// Reserve a row for the inline payout error so it doesn't overlap the card below (cards shift down
// transiently while the error is shown).
ImGui::Dummy(ImVec2(0, gap * 0.5f + (payoutBadThisFrame ? capFont->LegacySize + 2.0f * dp : 0.0f)));
}
} // namespace ui