refactor(audit): batch 4 — UI design-system helpers + i18n

Mechanical, behavior-preserving consolidations from the audit. Rendering
extractions were kept byte-equivalent; sites that couldn't be made identical
were left as-is (noted below).

Shared helpers:
- util::truncateMiddle(s,maxLen) / (s,front,back) in text_format.h — replaces
  6 file-local middle-ellipsis truncators + several inline substr sites
  (send/receive/transactions/balance_recent_tx/explorer + app.cpp + 3 dialogs).
  Carries the maxLen<=3 guard, fixing the latent unsigned-underflow copies.
- material::LoadingDots() in draw_helpers.h — one animated-ellipsis source for
  8 copy-pasted spinner sites (identical GetTime()*3 phase preserved).
- Reuse the existing FormatHashrate() in explorer_tab + peers_tab (dropped two
  inline hashrate ladders).
- material::DrawButtonGlassOverlay() — the glass-fill/rim/tactile overlay block
  shared by TactileButton / TactileSmallButton / schema TactileButton.
- material::CollapsibleHeader() — the invisible-button + label + chevron idiom
  (3 of 5 settings_page sites; RPC/Debug headers skipped — non-identical).
- material::GlassCardScope (RAII) — the ChannelsSplit/Indent/DrawGlassPanel/
  ChannelsMerge card scaffold (5 settings_page cards; About/send/receive skipped
  — different padding / logo interleaving).
- material::DialogWarningHeader()/DialogConfirmFooter() — warning header +
  50/50 Cancel/danger footer across the confirm dialogs; button height moved
  from a hardcoded 40px into ui.toml (components.overlay-dialog.confirm-btn-height).
- File-local enterLowSpec()/exitLowSpec() collapse the 3 low-spec snapshot copies.

i18n: wrapped hardcoded English in the shared balance render paths and the
whole Lite lifecycle/security section in TR(), with English defaults added to
loadBuiltinEnglish() (res/lang/*.json left for the translation tooling — TR
falls back to English, so output is unchanged).

Full-node + Lite build clean; ctest 1/1; hygiene clean. These are rendering
changes — the Settings page (cards/headers/confirm dialogs), all tactile
buttons, and the Lite settings section warrant a screenshot check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 17:29:12 -05:00
parent 280a71e973
commit de11850c74
21 changed files with 452 additions and 500 deletions

View File

@@ -170,6 +170,51 @@ struct SettingsPageState {
static SettingsPageState s_settingsState;
// Low-spec mode enter: snapshot the current effects prefs, then force the
// low-cost overrides. When applyEffects is true, also push the overrides to
// the live effects subsystems (the runtime-sync path passes false since it
// only mirrors an already-applied hotkey toggle into the checkbox state).
static void enterLowSpec(bool applyEffects) {
s_settingsState.low_spec_snapshot.valid = true;
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
s_settingsState.acrylic_enabled = false;
s_settingsState.blur_amount = 0.0f;
s_settingsState.ui_opacity = 1.0f;
s_settingsState.window_opacity = 1.0f;
s_settingsState.theme_effects_enabled = false;
s_settingsState.scanline_enabled = false;
if (applyEffects) {
effects::ImGuiAcrylic::ApplyBlurAmount(0.0f);
effects::ImGuiAcrylic::SetUIOpacity(1.0f);
effects::ThemeEffects::instance().setEnabled(false);
ConsoleTab::s_scanline_enabled = false;
}
}
// Low-spec mode exit: restore the previously snapshotted effects prefs and
// clear the snapshot. When applyEffects is true, also push them to the live
// effects subsystems. Callers guard this on low_spec_snapshot.valid.
static void exitLowSpec(bool applyEffects) {
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
if (applyEffects) {
effects::ImGuiAcrylic::ApplyBlurAmount(s_settingsState.blur_amount);
effects::ImGuiAcrylic::SetUIOpacity(s_settingsState.ui_opacity);
effects::ThemeEffects::instance().setEnabled(s_settingsState.theme_effects_enabled);
ConsoleTab::s_scanline_enabled = s_settingsState.scanline_enabled;
}
s_settingsState.low_spec_snapshot.valid = false;
}
// Count whitespace-separated words in a (seed) buffer — used to validate/guide restore input.
static int liteSeedWordCount(const char* s) {
int words = 0;
@@ -274,7 +319,7 @@ static void evaluateLiteLifecycleRequestFromPageState(App* app) {
}
if (started) {
s_settingsState.lite_lifecycle_pending = true;
s_settingsState.lite_lifecycle_status = "Working";
s_settingsState.lite_lifecycle_status = TR("lite_working");
s_settingsState.lite_lifecycle_summary.clear();
} else {
// Rejected before any thread launched (wallet already open, an attempt in flight, or no
@@ -428,28 +473,10 @@ void RenderSettingsPage(App* app) {
if (s_settingsState.low_spec_mode != runtimeLowSpec) {
if (runtimeLowSpec) {
// Hotkey turned low-spec ON — save snapshot, override statics
s_settingsState.low_spec_snapshot.valid = true;
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
s_settingsState.acrylic_enabled = false;
s_settingsState.blur_amount = 0.0f;
s_settingsState.ui_opacity = 1.0f;
s_settingsState.window_opacity = 1.0f;
s_settingsState.theme_effects_enabled = false;
s_settingsState.scanline_enabled = false;
enterLowSpec(false);
} else if (s_settingsState.low_spec_snapshot.valid) {
// Hotkey turned low-spec OFF — restore snapshot
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
s_settingsState.low_spec_snapshot.valid = false;
exitLowSpec(false);
} else if (app->settings()) {
// No snapshot — read prefs from settings file
s_settingsState.blur_amount = app->settings()->getBlurMultiplier();
@@ -561,12 +588,7 @@ void RenderSettingsPage(App* app) {
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("theme_language"));
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
ImVec2 cardMin = ImGui::GetCursorScreenPos();
dl->ChannelsSplit(2);
dl->ChannelsSetCurrent(1);
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
ImGui::Indent(pad);
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
float contentW = availWidth - pad * 2;
float comboGap = S.drawElement("components.settings-page", "combo-row-gap").size;
@@ -767,27 +789,9 @@ void RenderSettingsPage(App* app) {
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// --- Collapsible: Advanced Effects... ---
{
const char* arrow = s_settingsState.effects_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
ImGui::PushFont(body2);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f));
{
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
if (ImGui::Button("##EffectsToggle", ImVec2(contentW, ImGui::GetFrameHeight()))) {
s_settingsState.effects_expanded = !s_settingsState.effects_expanded;
}
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("advanced_effects"));
ImFont* iconFont = Type().iconSmall();
if (!iconFont) iconFont = body2;
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + contentW - arrowW, textY), OnSurfaceMedium(), arrow);
}
ImGui::PopStyleColor(3);
ImGui::PopFont();
}
material::CollapsibleHeader(dl, "##EffectsToggle", TR("advanced_effects"),
s_settingsState.effects_expanded, contentW,
body2, OnSurfaceMedium());
if (s_settingsState.effects_expanded) {
ImGui::PushFont(body2);
@@ -796,35 +800,9 @@ void RenderSettingsPage(App* app) {
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &s_settingsState.low_spec_mode)) {
effects::setLowSpecMode(s_settingsState.low_spec_mode);
if (s_settingsState.low_spec_mode) {
s_settingsState.low_spec_snapshot.valid = true;
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
s_settingsState.acrylic_enabled = false;
s_settingsState.blur_amount = 0.0f;
s_settingsState.ui_opacity = 1.0f;
s_settingsState.window_opacity = 1.0f;
s_settingsState.theme_effects_enabled = false;
s_settingsState.scanline_enabled = false;
effects::ImGuiAcrylic::ApplyBlurAmount(0.0f);
effects::ImGuiAcrylic::SetUIOpacity(1.0f);
effects::ThemeEffects::instance().setEnabled(false);
ConsoleTab::s_scanline_enabled = false;
enterLowSpec(true);
} else if (s_settingsState.low_spec_snapshot.valid) {
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
effects::ImGuiAcrylic::ApplyBlurAmount(s_settingsState.blur_amount);
effects::ImGuiAcrylic::SetUIOpacity(s_settingsState.ui_opacity);
effects::ThemeEffects::instance().setEnabled(s_settingsState.theme_effects_enabled);
ConsoleTab::s_scanline_enabled = s_settingsState.scanline_enabled;
s_settingsState.low_spec_snapshot.valid = false;
exitLowSpec(true);
}
saveSettingsPageState(app->settings());
}
@@ -1065,26 +1043,10 @@ void RenderSettingsPage(App* app) {
// --- Collapsible: Advanced Effects... ---
{
const char* arrow = s_settingsState.effects_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
ImGui::PushFont(body2);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f));
{
float narrowContentW = availWidth - pad * 2;
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
if (ImGui::Button("##EffectsToggleN", ImVec2(narrowContentW, ImGui::GetFrameHeight()))) {
s_settingsState.effects_expanded = !s_settingsState.effects_expanded;
}
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("advanced_effects"));
ImFont* iconFont = Type().iconSmall();
if (!iconFont) iconFont = body2;
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + narrowContentW - arrowW, textY), OnSurfaceMedium(), arrow);
}
ImGui::PopStyleColor(3);
ImGui::PopFont();
float narrowContentW = availWidth - pad * 2;
material::CollapsibleHeader(dl, "##EffectsToggleN", TR("advanced_effects"),
s_settingsState.effects_expanded, narrowContentW,
body2, OnSurfaceMedium());
}
if (s_settingsState.effects_expanded) {
@@ -1093,35 +1055,9 @@ void RenderSettingsPage(App* app) {
if (ImGui::Checkbox(TrId("low_spec_mode", "low_spec").c_str(), &s_settingsState.low_spec_mode)) {
effects::setLowSpecMode(s_settingsState.low_spec_mode);
if (s_settingsState.low_spec_mode) {
s_settingsState.low_spec_snapshot.valid = true;
s_settingsState.low_spec_snapshot.acrylic_enabled = s_settingsState.acrylic_enabled;
s_settingsState.low_spec_snapshot.blur_amount = s_settingsState.blur_amount;
s_settingsState.low_spec_snapshot.ui_opacity = s_settingsState.ui_opacity;
s_settingsState.low_spec_snapshot.window_opacity = s_settingsState.window_opacity;
s_settingsState.low_spec_snapshot.theme_effects_enabled = s_settingsState.theme_effects_enabled;
s_settingsState.low_spec_snapshot.scanline_enabled = s_settingsState.scanline_enabled;
s_settingsState.acrylic_enabled = false;
s_settingsState.blur_amount = 0.0f;
s_settingsState.ui_opacity = 1.0f;
s_settingsState.window_opacity = 1.0f;
s_settingsState.theme_effects_enabled = false;
s_settingsState.scanline_enabled = false;
effects::ImGuiAcrylic::ApplyBlurAmount(0.0f);
effects::ImGuiAcrylic::SetUIOpacity(1.0f);
effects::ThemeEffects::instance().setEnabled(false);
ConsoleTab::s_scanline_enabled = false;
enterLowSpec(true);
} else if (s_settingsState.low_spec_snapshot.valid) {
s_settingsState.blur_amount = s_settingsState.low_spec_snapshot.blur_amount;
s_settingsState.acrylic_enabled = (s_settingsState.blur_amount > 0.001f);
s_settingsState.ui_opacity = s_settingsState.low_spec_snapshot.ui_opacity;
s_settingsState.window_opacity = s_settingsState.low_spec_snapshot.window_opacity;
s_settingsState.theme_effects_enabled = s_settingsState.low_spec_snapshot.theme_effects_enabled;
s_settingsState.scanline_enabled = s_settingsState.low_spec_snapshot.scanline_enabled;
effects::ImGuiAcrylic::ApplyBlurAmount(s_settingsState.blur_amount);
effects::ImGuiAcrylic::SetUIOpacity(s_settingsState.ui_opacity);
effects::ThemeEffects::instance().setEnabled(s_settingsState.theme_effects_enabled);
ConsoleTab::s_scanline_enabled = s_settingsState.scanline_enabled;
s_settingsState.low_spec_snapshot.valid = false;
exitLowSpec(true);
}
saveSettingsPageState(app->settings());
}
@@ -1219,19 +1155,6 @@ void RenderSettingsPage(App* app) {
ImGui::PopFont();
} // s_settingsState.effects_expanded
}
// Bottom padding
ImGui::Dummy(ImVec2(0, bottomPad));
ImGui::Unindent(pad);
// Draw glass panel behind content (auto-sized)
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
dl->ChannelsSetCurrent(0);
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
dl->ChannelsMerge();
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
ImGui::Dummy(ImVec2(availWidth, 0));
}
ImGui::Dummy(ImVec2(0, gap));
@@ -1243,11 +1166,8 @@ void RenderSettingsPage(App* app) {
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("wallet"));
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
ImVec2 cardMin = ImGui::GetCursorScreenPos();
dl->ChannelsSplit(2);
dl->ChannelsSetCurrent(1);
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
ImGui::Indent(pad);
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
const ImVec2& cardMin = card.cardMin;
float contentW = availWidth - pad * 2;
@@ -1307,27 +1227,9 @@ void RenderSettingsPage(App* app) {
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// --- Collapsible: Tools & Actions... ---
{
const char* arrow = s_settingsState.tools_expanded ? ICON_MD_EXPAND_LESS : ICON_MD_EXPAND_MORE;
ImGui::PushFont(body2);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f));
{
ImVec2 hdrPos = ImGui::GetCursorScreenPos();
if (ImGui::Button("##ToolsToggle", ImVec2(contentW, ImGui::GetFrameHeight()))) {
s_settingsState.tools_expanded = !s_settingsState.tools_expanded;
}
float textY = hdrPos.y + (ImGui::GetFrameHeight() - body2->LegacySize) * 0.5f;
dl->AddText(body2, body2->LegacySize, ImVec2(hdrPos.x, textY), OnSurfaceMedium(), TR("tools_actions"));
ImFont* iconFont = Type().iconSmall();
if (!iconFont) iconFont = body2;
float arrowW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, arrow).x;
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(hdrPos.x + contentW - arrowW, textY), OnSurfaceMedium(), arrow);
}
ImGui::PopStyleColor(3);
ImGui::PopFont();
}
material::CollapsibleHeader(dl, "##ToolsToggle", TR("tools_actions"),
s_settingsState.tools_expanded, contentW,
body2, OnSurfaceMedium());
if (s_settingsState.tools_expanded) {
float btnSpacing = Layout::spacingMd();
@@ -1435,17 +1337,6 @@ void RenderSettingsPage(App* app) {
if (scale < 1.0f) ImGui::SetWindowFontScale(1.0f);
}
ImGui::Dummy(ImVec2(0, bottomPad));
ImGui::Unindent(pad);
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
dl->ChannelsSetCurrent(0);
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
dl->ChannelsMerge();
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
ImGui::Dummy(ImVec2(availWidth, 0));
}
ImGui::Dummy(ImVec2(0, gap));
@@ -1458,11 +1349,7 @@ void RenderSettingsPage(App* app) {
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("node_security"));
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
ImVec2 cardMin = ImGui::GetCursorScreenPos();
dl->ChannelsSplit(2);
dl->ChannelsSetCurrent(1);
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
ImGui::Indent(pad);
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
float contentW = availWidth - pad * 2;
float minBtnW = S.drawElement("components.settings-page", "wallet-btn-min-width").sizeOr(130.0f);
@@ -1618,18 +1505,18 @@ void RenderSettingsPage(App* app) {
// Lite-server selection lives in the dedicated Network tab now.
Type().textColored(TypeStyle::Body2, OnSurfaceMedium(),
"Lite servers are managed in the Network tab.");
TR("lite_servers_network_tab"));
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
if (ImGui::Button("Lite wallet request##LiteLifecycleToggle", ImVec2(liteInputW, 0))) {
if (ImGui::Button(TrId("lite_wallet_request", "LiteLifecycleToggle").c_str(), ImVec2(liteInputW, 0))) {
s_settingsState.lite_lifecycle_expanded = !s_settingsState.lite_lifecycle_expanded;
}
if (s_settingsState.lite_lifecycle_expanded) {
const char* lifecycleLabels[] = {"Create", "Open", "Restore"};
const char* lifecycleLabels[] = {TR("lite_op_create"), TR("lite_op_open"), TR("lite_op_restore")};
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Action");
ImGui::TextUnformatted(TR("lite_action"));
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
ImGui::SetNextItemWidth(liteInputW);
if (ImGui::BeginCombo("##LiteLifecycleOperation",
@@ -1650,7 +1537,7 @@ void RenderSettingsPage(App* app) {
s_settingsState.lite_lifecycle_operation == 2) {
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Wallet");
ImGui::TextUnformatted(TR("lite_wallet_label"));
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
ImGui::SetNextItemWidth(liteInputW);
ImGui::InputText("##LiteWalletPath", s_settingsState.lite_wallet_path,
@@ -1660,7 +1547,7 @@ void RenderSettingsPage(App* app) {
if (s_settingsState.lite_lifecycle_operation == 2) {
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Seed");
ImGui::TextUnformatted(TR("lite_seed_label"));
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
ImGui::SetNextItemWidth(liteInputW);
ImGui::InputText("##LiteRestoreSeed", s_settingsState.lite_restore_seed,
@@ -1670,38 +1557,38 @@ void RenderSettingsPage(App* app) {
{
const int wc = liteSeedWordCount(s_settingsState.lite_restore_seed);
char wbuf[32];
snprintf(wbuf, sizeof(wbuf), "%d / 24 words", wc);
snprintf(wbuf, sizeof(wbuf), TR("lite_word_count"), wc);
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
Type().textColored(TypeStyle::Caption, wc == 24 ? Success() : Warning(), wbuf);
}
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Birthday");
ImGui::TextUnformatted(TR("lite_birthday_label"));
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
ImGui::SetNextItemWidth(std::min(160.0f, liteInputW));
ImGui::InputInt("##LiteRestoreBirthday", &s_settingsState.lite_restore_birthday);
if (s_settingsState.lite_restore_birthday < 0) s_settingsState.lite_restore_birthday = 0;
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(),
"Block height to start scanning from. Leave 0 if unknown (slower full scan).");
TR("lite_birthday_hint"));
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Account");
ImGui::TextUnformatted(TR("lite_account_label"));
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
ImGui::SetNextItemWidth(std::min(160.0f, liteInputW));
ImGui::InputInt("##LiteRestoreAccount", &s_settingsState.lite_restore_account);
if (s_settingsState.lite_restore_account < 0) s_settingsState.lite_restore_account = 0;
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::Checkbox("Overwrite##LiteRestoreOverwrite",
ImGui::Checkbox(TrId("lite_overwrite", "LiteRestoreOverwrite").c_str(),
&s_settingsState.lite_restore_overwrite);
}
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Passphrase");
ImGui::TextUnformatted(TR("lite_passphrase_label"));
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
ImGui::SetNextItemWidth(liteInputW);
ImGui::InputText("##LiteLifecyclePassphrase",
@@ -1719,7 +1606,7 @@ void RenderSettingsPage(App* app) {
const auto& result = lite->lastLifecycleResult();
s_settingsState.lite_lifecycle_summary = result.bridgeResponseRedacted;
if (result.walletReady) {
s_settingsState.lite_lifecycle_status = "Wallet ready";
s_settingsState.lite_lifecycle_status = TR("lite_wallet_ready");
} else {
s_settingsState.lite_lifecycle_status = result.error.empty()
? result.status.message
@@ -1735,7 +1622,7 @@ void RenderSettingsPage(App* app) {
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
const bool liteLifecycleBusy = s_settingsState.lite_lifecycle_pending;
if (liteLifecycleBusy) ImGui::BeginDisabled();
if (TactileButton("Validate##LiteLifecycleValidate", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_validate", "LiteLifecycleValidate").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
evaluateLiteLifecycleRequestFromPageState(app);
}
if (liteLifecycleBusy) ImGui::EndDisabled();
@@ -1765,17 +1652,15 @@ void RenderSettingsPage(App* app) {
if (app->liteWallet() && app->liteWallet()->walletOpen()) {
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
Type().text(TypeStyle::Body2, "Backup & keys");
Type().text(TypeStyle::Body2, TR("lite_backup_keys"));
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
if (TactileButton("Show seed##LiteExportSeed", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_show_seed", "LiteExportSeed").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
auto r = app->liteWallet()->exportSeed();
wallet::secureWipeLiteSecret(s_settingsState.lite_export_secret);
if (r.ok) {
s_settingsState.lite_export_secret = r.seedPhrase;
s_settingsState.lite_export_label =
"Seed phrase — the ONLY way to restore your wallet. "
"Write it down, store it offline, never share it.";
s_settingsState.lite_export_label = TR("lite_seed_warning");
s_settingsState.lite_export_is_seed = true;
s_settingsState.lite_export_birthday = r.birthday;
s_settingsState.lite_backup_status.clear();
@@ -1787,13 +1672,13 @@ void RenderSettingsPage(App* app) {
wallet::secureWipeLiteSecret(r.seedPhrase); // wipe the result copy
}
ImGui::SameLine(0, Layout::spacingSm());
if (TactileButton("Show private keys##LiteExportKeys", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_show_private_keys", "LiteExportKeys").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
auto r = app->liteWallet()->exportPrivateKeys();
wallet::secureWipeLiteSecret(s_settingsState.lite_export_secret);
s_settingsState.lite_export_is_seed = false; // keys, not a seed
if (r.ok) {
s_settingsState.lite_export_secret = r.privateKeysJson;
s_settingsState.lite_export_label = "Private keys — anyone with these can spend your funds";
s_settingsState.lite_export_label = TR("lite_private_keys_warning");
s_settingsState.lite_backup_status.clear();
} else {
s_settingsState.lite_export_label.clear();
@@ -1814,19 +1699,19 @@ void RenderSettingsPage(App* app) {
// The seed's birthday is needed to restore quickly — show + back it up too.
if (s_settingsState.lite_export_is_seed) {
char bday[64];
snprintf(bday, sizeof(bday), "Birthday: %llu (back this up too)",
snprintf(bday, sizeof(bday), TR("lite_birthday_backup"),
s_settingsState.lite_export_birthday);
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), bday);
}
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
if (TactileButton("Copy##LiteExportCopy", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_copy", "LiteExportCopy").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
app->copySecretToClipboard(s_settingsState.lite_export_secret);
}
ImGui::SameLine(0, Layout::spacingSm());
// Save the seed (+ birthday) to an owner-only (0600) file in the config dir.
if (s_settingsState.lite_export_is_seed &&
TactileButton("Save to file##LiteExportSave", ImVec2(0, 0), S.resolveFont("button"))) {
TactileButton(TrId("lite_save_to_file", "LiteExportSave").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
std::string path = util::Platform::getConfigDir() + "/lite-seed-backup.txt";
std::string content = s_settingsState.lite_export_secret + "\nBirthday: " +
std::to_string(s_settingsState.lite_export_birthday) + "\n";
@@ -1834,11 +1719,11 @@ void RenderSettingsPage(App* app) {
path, content, /*restrictPermissions=*/true);
wallet::secureWipeLiteSecret(content);
s_settingsState.lite_backup_status = ok
? ("Saved (plaintext, owner-only) to " + path)
: ("Could not write " + path);
? (std::string(TR("lite_saved_to")) + path)
: (std::string(TR("lite_could_not_write")) + path);
}
if (s_settingsState.lite_export_is_seed) ImGui::SameLine(0, Layout::spacingSm());
if (TactileButton("Hide & wipe##LiteExportHide", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_hide_wipe", "LiteExportHide").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
wallet::secureWipeLiteSecret(s_settingsState.lite_export_secret);
s_settingsState.lite_export_label.clear();
s_settingsState.lite_export_is_seed = false;
@@ -1848,18 +1733,18 @@ void RenderSettingsPage(App* app) {
// Import a spending/viewing key (history appears after the next sync).
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Import key");
ImGui::TextUnformatted(TR("lite_import_key_label"));
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
ImGui::SetNextItemWidth(liteInputW);
ImGui::InputText("##LiteImportKey", s_settingsState.lite_import_key,
sizeof(s_settingsState.lite_import_key),
ImGuiInputTextFlags_Password);
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
if (TactileButton("Import##LiteImportKeyBtn", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_import", "LiteImportKeyBtn").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
const auto r = app->liteWallet()->importKey(s_settingsState.lite_import_key);
sodium_memzero(s_settingsState.lite_import_key, sizeof(s_settingsState.lite_import_key));
s_settingsState.lite_backup_status =
r.ok ? "Key imported — run a sync to scan its history" : r.error;
r.ok ? TR("lite_key_imported") : r.error;
}
if (!s_settingsState.lite_backup_status.empty()) {
@@ -1871,57 +1756,57 @@ void RenderSettingsPage(App* app) {
// ---- Security: passphrase encryption (encrypt / unlock / lock / decrypt) ----
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
Type().text(TypeStyle::Body2, "Security");
Type().text(TypeStyle::Body2, TR("lite_security"));
const auto& wstate = app->getWalletState();
const float encLabelX = leftX - sectionOrigin.x + liteLabelW;
if (!wstate.isEncrypted()) {
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Passphrase");
ImGui::TextUnformatted(TR("lite_passphrase_label"));
ImGui::SameLine(encLabelX);
ImGui::SetNextItemWidth(liteInputW);
ImGui::InputText("##LiteEncryptPass", s_settingsState.lite_enc_pass,
sizeof(s_settingsState.lite_enc_pass), ImGuiInputTextFlags_Password);
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
if (TactileButton("Encrypt wallet##LiteEncrypt", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_encrypt_wallet", "LiteEncrypt").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
const auto r = app->liteWallet()->encryptWallet(s_settingsState.lite_enc_pass);
sodium_memzero(s_settingsState.lite_enc_pass, sizeof(s_settingsState.lite_enc_pass));
s_settingsState.lite_encryption_status = r.ok ? "Wallet encrypted" : r.error;
s_settingsState.lite_encryption_status = r.ok ? TR("lite_wallet_encrypted") : r.error;
}
} else {
if (wstate.isLocked()) {
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Unlock");
ImGui::TextUnformatted(TR("lite_unlock"));
ImGui::SameLine(encLabelX);
ImGui::SetNextItemWidth(liteInputW);
ImGui::InputText("##LiteUnlockPass", s_settingsState.lite_enc_pass,
sizeof(s_settingsState.lite_enc_pass), ImGuiInputTextFlags_Password);
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
if (TactileButton("Unlock##LiteUnlock", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_unlock", "LiteUnlock").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
const bool ok = app->liteWallet()->unlockWallet(s_settingsState.lite_enc_pass);
sodium_memzero(s_settingsState.lite_enc_pass, sizeof(s_settingsState.lite_enc_pass));
s_settingsState.lite_encryption_status = ok ? "Wallet unlocked" : "Unlock failed";
s_settingsState.lite_encryption_status = ok ? TR("lite_wallet_unlocked") : TR("lite_unlock_failed");
}
} else {
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
if (TactileButton("Lock now##LiteLock", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_lock_now", "LiteLock").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
s_settingsState.lite_encryption_status =
app->liteWallet()->lockWallet() ? "Wallet locked" : "Lock failed";
app->liteWallet()->lockWallet() ? TR("lite_wallet_locked") : TR("lite_lock_failed");
}
}
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Passphrase");
ImGui::TextUnformatted(TR("lite_passphrase_label"));
ImGui::SameLine(encLabelX);
ImGui::SetNextItemWidth(liteInputW);
ImGui::InputText("##LiteDecryptPass", s_settingsState.lite_dec_pass,
sizeof(s_settingsState.lite_dec_pass), ImGuiInputTextFlags_Password);
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
if (TactileButton("Remove encryption##LiteDecrypt", ImVec2(0, 0), S.resolveFont("button"))) {
if (TactileButton(TrId("lite_remove_encryption", "LiteDecrypt").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
const auto r = app->liteWallet()->decryptWallet(s_settingsState.lite_dec_pass);
sodium_memzero(s_settingsState.lite_dec_pass, sizeof(s_settingsState.lite_dec_pass));
s_settingsState.lite_encryption_status = r.ok ? "Encryption removed" : r.error;
s_settingsState.lite_encryption_status = r.ok ? TR("lite_encryption_removed") : r.error;
}
}
if (!s_settingsState.lite_encryption_status.empty()) {
@@ -2347,17 +2232,6 @@ void RenderSettingsPage(App* app) {
ImGui::SetCursorScreenPos(ImVec2(sectionOrigin.x, ImGui::GetCursorScreenPos().y));
}
}
ImGui::Dummy(ImVec2(0, bottomPad));
ImGui::Unindent(pad);
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
dl->ChannelsSetCurrent(0);
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
dl->ChannelsMerge();
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
ImGui::Dummy(ImVec2(availWidth, 0));
}
ImGui::Dummy(ImVec2(0, gap));
@@ -2369,11 +2243,7 @@ void RenderSettingsPage(App* app) {
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("explorer_section"));
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
ImVec2 cardMin = ImGui::GetCursorScreenPos();
dl->ChannelsSplit(2);
dl->ChannelsSetCurrent(1);
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
ImGui::Indent(pad);
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
float contentW = availWidth - pad * 2;
ImGui::PushFont(body2);
@@ -2413,17 +2283,6 @@ void RenderSettingsPage(App* app) {
util::Platform::openUrl("https://explorer.dragonx.is");
}
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_block_explorer"));
ImGui::Dummy(ImVec2(0, bottomPad));
ImGui::Unindent(pad);
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
dl->ChannelsSetCurrent(0);
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
dl->ChannelsMerge();
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
ImGui::Dummy(ImVec2(availWidth, 0));
}
ImGui::Dummy(ImVec2(0, gap));
@@ -2597,11 +2456,7 @@ void RenderSettingsPage(App* app) {
if (s_settingsState.debug_expanded) {
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
ImVec2 cardMin = ImGui::GetCursorScreenPos();
dl->ChannelsSplit(2);
dl->ChannelsSetCurrent(1);
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + pad));
ImGui::Indent(pad);
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
ImGui::TextColored(ImVec4(1,1,1,0.5f), "%s", TR("settings_debug_select"));
ImGui::TextColored(ImVec4(1,1,1,0.35f), "%s", TR("settings_debug_restart_note"));
@@ -2684,17 +2539,6 @@ void RenderSettingsPage(App* app) {
}
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_restart_daemon"));
}
ImGui::Dummy(ImVec2(0, bottomPad));
ImGui::Unindent(pad);
ImVec2 cardMax(cardMin.x + availWidth, ImGui::GetCursorScreenPos().y);
dl->ChannelsSetCurrent(0);
DrawGlassPanel(dl, cardMin, cardMax, glassSpec);
dl->ChannelsMerge();
ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
ImGui::Dummy(ImVec2(availWidth, 0));
}
}
@@ -2739,12 +2583,8 @@ void RenderSettingsPage(App* app) {
// Confirmation dialog for clearing z-tx history
if (s_settingsState.confirm_clear_ztx) {
if (BeginOverlayDialog(TR("confirm_clear_ztx_title"), &s_settingsState.confirm_clear_ztx, 480.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "%s", TR("warning"));
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.6f, 0.0f, 1.0f));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_clear_ztx_warning1"));
ImGui::Spacing();
@@ -2752,15 +2592,14 @@ void RenderSettingsPage(App* app) {
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TR("cancel"), ImVec2(btnW, 40))) {
bool doCancel = false, doConfirm = false;
material::DialogConfirmFooter(TR("cancel"), TrId("clear_anyway", "clear_ztx_btn").c_str(),
true, doCancel, doConfirm);
if (doCancel) {
s_settingsState.confirm_clear_ztx = false;
}
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.3f, 0.3f, 1.0f));
if (ImGui::Button(TrId("clear_anyway", "clear_ztx_btn").c_str(), ImVec2(btnW, 40))) {
if (doConfirm) {
std::string ztx_file = util::Platform::getDragonXDataDir() + "ztx_history.json";
if (util::Platform::deleteFile(ztx_file)) {
Notifications::instance().success("Z-transaction history cleared");
@@ -2769,7 +2608,6 @@ void RenderSettingsPage(App* app) {
}
s_settingsState.confirm_clear_ztx = false;
}
ImGui::PopStyleColor(2);
EndOverlayDialog();
}
}
@@ -2777,12 +2615,8 @@ void RenderSettingsPage(App* app) {
// Confirmation dialog for deleting blockchain data
if (s_settingsState.confirm_delete_blockchain) {
if (BeginOverlayDialog(TR("confirm_delete_blockchain_title"), &s_settingsState.confirm_delete_blockchain, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", TR("warning"));
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_delete_blockchain_msg"));
ImGui::Spacing();
@@ -2790,20 +2624,18 @@ void RenderSettingsPage(App* app) {
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TR("cancel"), ImVec2(btnW, 40))) {
bool doCancel = false, doConfirm = false;
material::DialogConfirmFooter(TR("cancel"), TrId("delete_blockchain_confirm", "del_bc_btn").c_str(),
true, doCancel, doConfirm);
if (doCancel) {
s_settingsState.confirm_delete_blockchain = false;
}
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.3f, 0.3f, 1.0f));
if (ImGui::Button(TrId("delete_blockchain_confirm", "del_bc_btn").c_str(), ImVec2(btnW, 40))) {
if (doConfirm) {
if (app->supportsFullNodeLifecycleActions())
app->deleteBlockchainData();
s_settingsState.confirm_delete_blockchain = false;
}
ImGui::PopStyleColor(2);
EndOverlayDialog();
}
}
@@ -2825,11 +2657,7 @@ void RenderSettingsPage(App* app) {
}
if (BeginOverlayDialog(TR("confirm_rescan_title"), &s_settingsState.confirm_rescan, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
ImGui::Spacing();
@@ -2877,11 +2705,7 @@ void RenderSettingsPage(App* app) {
// Confirm: repair wallet (-zapwallettxes=2 — wipe & rebuild wallet tx records, then rescan)
if (s_settingsState.confirm_repair_wallet) {
if (BeginOverlayDialog(TR("confirm_repair_wallet_title"), &s_settingsState.confirm_repair_wallet, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_repair_wallet_msg"));
@@ -2891,12 +2715,14 @@ void RenderSettingsPage(App* app) {
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TrId("cancel", "repair_wallet_cancel").c_str(), ImVec2(btnW, 40))) {
bool doCancel = false, doConfirm = false;
material::DialogConfirmFooter(TrId("cancel", "repair_wallet_cancel").c_str(),
TrId("repair_wallet", "repair_wallet_confirm").c_str(),
false, doCancel, doConfirm);
if (doCancel) {
s_settingsState.confirm_repair_wallet = false;
}
ImGui::SameLine();
if (ImGui::Button(TrId("repair_wallet", "repair_wallet_confirm").c_str(), ImVec2(btnW, 40))) {
if (doConfirm) {
app->repairWallet();
s_settingsState.confirm_repair_wallet = false;
}
@@ -2907,11 +2733,7 @@ void RenderSettingsPage(App* app) {
// Confirm: reinstall the bundled daemon binary (stop → overwrite → restart)
if (s_settingsState.confirm_reinstall_daemon) {
if (BeginOverlayDialog(TR("confirm_reinstall_daemon_title"), &s_settingsState.confirm_reinstall_daemon, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_reinstall_daemon_msg"));
@@ -2921,12 +2743,14 @@ void RenderSettingsPage(App* app) {
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TrId("cancel", "reinstall_daemon_cancel").c_str(), ImVec2(btnW, 40))) {
bool doCancel = false, doConfirm = false;
material::DialogConfirmFooter(TrId("cancel", "reinstall_daemon_cancel").c_str(),
TrId("daemon_install_bundled", "reinstall_daemon_confirm").c_str(),
false, doCancel, doConfirm);
if (doCancel) {
s_settingsState.confirm_reinstall_daemon = false;
}
ImGui::SameLine();
if (ImGui::Button(TrId("daemon_install_bundled", "reinstall_daemon_confirm").c_str(), ImVec2(btnW, 40))) {
if (doConfirm) {
app->reinstallBundledDaemon();
s_settingsState.daemon_info_loaded = false; // refresh the panel after the swap
s_settingsState.confirm_reinstall_daemon = false;
@@ -2938,11 +2762,7 @@ void RenderSettingsPage(App* app) {
// Confirm: restart daemon (briefly drops the connection to apply changed options)
if (s_settingsState.confirm_restart_daemon) {
if (BeginOverlayDialog(TR("confirm_restart_daemon_title"), &s_settingsState.confirm_restart_daemon, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_restart_daemon_msg"));
@@ -2950,12 +2770,14 @@ void RenderSettingsPage(App* app) {
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TrId("cancel", "restartd_cancel").c_str(), ImVec2(btnW, 40))) {
bool doCancel = false, doConfirm = false;
material::DialogConfirmFooter(TrId("cancel", "restartd_cancel").c_str(),
TrId("settings_restart_daemon", "restartd_confirm").c_str(),
false, doCancel, doConfirm);
if (doCancel) {
s_settingsState.confirm_restart_daemon = false;
}
ImGui::SameLine();
if (ImGui::Button(TrId("settings_restart_daemon", "restartd_confirm").c_str(), ImVec2(btnW, 40))) {
if (doConfirm) {
s_settingsState.debug_cats_dirty = false;
app->restartDaemon();
s_settingsState.confirm_restart_daemon = false;
@@ -2967,11 +2789,7 @@ void RenderSettingsPage(App* app) {
// Confirm: lite wallet re-download blocks (rescan from the lite server — long but safe)
if (s_settingsState.confirm_lite_redownload) {
if (BeginOverlayDialog(TR("confirm_lite_redownload_title"), &s_settingsState.confirm_lite_redownload, 500.0f, 0.94f)) {
ImGui::PushFont(Type().iconLarge());
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), ICON_MD_WARNING);
ImGui::PopFont();
ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "%s", TR("warning"));
material::DialogWarningHeader(TR("warning"), ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
ImGui::Spacing();
ImGui::TextWrapped("%s", TR("confirm_lite_redownload_msg"));
@@ -2981,12 +2799,14 @@ void RenderSettingsPage(App* app) {
ImGui::Separator();
ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button(TrId("cancel", "lite_redl_cancel").c_str(), ImVec2(btnW, 40))) {
bool doCancel = false, doConfirm = false;
material::DialogConfirmFooter(TrId("cancel", "lite_redl_cancel").c_str(),
TrId("lite_redownload_blocks", "lite_redl_confirm").c_str(),
false, doCancel, doConfirm);
if (doCancel) {
s_settingsState.confirm_lite_redownload = false;
}
ImGui::SameLine();
if (ImGui::Button(TrId("lite_redownload_blocks", "lite_redl_confirm").c_str(), ImVec2(btnW, 40))) {
if (doConfirm) {
if (auto* lite = app->liteWallet()) lite->startRescan();
s_settingsState.confirm_lite_redownload = false;
}