feat(settings): redesign the migrate-to-seed modal onto reference design
Migrate App::renderSeedMigrationDialog onto the settings-modal reference design.
This is the most fund-critical dialog in the app (create isolated seed wallet ->
z_mergetoaddress sweep -> confirm -> adopt/rescan), already fully i18n'd and
dp-scaled, so the change is purely the presentation shell:
- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth 580,
idSuffix "migrate"}) replacing the legacy positional overload
- all 26 StyledButton -> TactileButton (identical signature; height-0 auto-sizing)
- remove all 13 footer Separator dividers
- ShowSeed seed-phrase warning -> material::DialogWarningHeader (red text -> amber
⚠ header, matching the export-key reference dialog)
The fund pyramid is byte-for-byte unchanged: the busy/dismiss veto, the wipeSeed
(sodium_memzero) + close lambdas and the post-EndOverlayDialog seed scrub, all
seed_migration_step_ transitions, begin CreateSeedWallet/SweepToSeedWallet/
AdoptSeedWallet, refreshSeedMigrationBalance, pollSweepStatus, the snprintf
balance/confs/remaining formatting, both confirmation checkboxes and their gates,
RenderSeedWordGrid, copySecretToClipboard, writeFileAtomically, and the Discard
remove_all + settings cleanup.
Verified: build + ctest + hygiene clean; Spacing() count unchanged (28->28,
proving no collateral deletion); StyledButtons elsewhere in app.cpp untouched
(the swap was range-bounded to this function); a 3-lens adversarial review
(fund-path-untouched / secret-hygiene / widget-visual) returned zero findings,
with the fund-path lens proving the logic diffs byte-for-byte identical to HEAD.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
83
src/app.cpp
83
src/app.cpp
@@ -3524,7 +3524,11 @@ void App::renderSeedMigrationDialog()
|
||||
|| seed_migration_step_ == SeedMigrationStep::Sweeping
|
||||
|| seed_migration_step_ == SeedMigrationStep::Adopting;
|
||||
|
||||
if (!ui::material::BeginOverlayDialog(TR("mig_title"), &show_seed_migration_, 580.0f, 0.94f)) {
|
||||
ui::material::OverlayDialogSpec migOv;
|
||||
migOv.title = TR("mig_title"); migOv.p_open = &show_seed_migration_;
|
||||
migOv.style = ui::material::OverlayStyle::BlurFloat;
|
||||
migOv.cardWidth = 580.0f; migOv.idSuffix = "migrate";
|
||||
if (!ui::material::BeginOverlayDialog(migOv)) {
|
||||
if (!busy) wipeSeed();
|
||||
return;
|
||||
}
|
||||
@@ -3544,14 +3548,14 @@ void App::renderSeedMigrationDialog()
|
||||
// probe found so we never offer a pointless (already-seeded) or impossible (old daemon) run.
|
||||
if (!isConnected()) {
|
||||
ImGui::TextWrapped("%s", TR("mig_not_connected"));
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
ImGui::Spacing();
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
break;
|
||||
}
|
||||
if (state_.isLocked()) {
|
||||
ImGui::TextWrapped("%s", TR("mig_unlock_to_migrate"));
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
ImGui::Spacing();
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
break;
|
||||
}
|
||||
if (!seed_migration_precheck_started_ && !capture_mode_) beginSeedMigrationPrecheck();
|
||||
@@ -3564,37 +3568,37 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::SuccessVec4());
|
||||
ImGui::TextWrapped("%s", TR("mig_already_mnemonic"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("mig_backup_instead"), ImVec2(200 * dp, 0))) {
|
||||
ImGui::Spacing();
|
||||
if (ui::material::TactileButton(TR("mig_backup_instead"), ImVec2(200 * dp, 0))) {
|
||||
close();
|
||||
showSeedBackupDialog();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
break;
|
||||
case SeedMigrationPrecheck::DaemonTooOld:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
||||
ImGui::TextWrapped("%s", TR("mig_daemon_too_old"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
ImGui::Spacing();
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
break;
|
||||
case SeedMigrationPrecheck::CheckFailed:
|
||||
ImGui::TextWrapped("%s", TR("mig_check_failed"));
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("mig_recheck"), ImVec2(120 * dp, 0)))
|
||||
ImGui::Spacing();
|
||||
if (ui::material::TactileButton(TR("mig_recheck"), ImVec2(120 * dp, 0)))
|
||||
seed_migration_precheck_started_ = false; // re-probe next frame
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||
break;
|
||||
case SeedMigrationPrecheck::Legacy:
|
||||
default:
|
||||
ImGui::TextWrapped("%s", TR("mig_intro"));
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("mig_create"), ImVec2(180 * dp, 0)))
|
||||
ImGui::Spacing();
|
||||
if (ui::material::TactileButton(TR("mig_create"), ImVec2(180 * dp, 0)))
|
||||
beginCreateSeedWallet();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("cancel"), ImVec2(120 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("cancel"), ImVec2(120 * dp, 0)))
|
||||
close();
|
||||
break;
|
||||
}
|
||||
@@ -3608,19 +3612,17 @@ void App::renderSeedMigrationDialog()
|
||||
break;
|
||||
|
||||
case SeedMigrationStep::ShowSeed: {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
||||
ImGui::TextWrapped("%s", TR("mig_seed_warning"));
|
||||
ImGui::PopStyleColor();
|
||||
ui::material::DialogWarningHeader(TR("mig_seed_warning"));
|
||||
ImGui::Spacing();
|
||||
ui::RenderSeedWordGrid(ui::SplitSeedWords(seed_migration_seed_));
|
||||
ImGui::Spacing();
|
||||
ImGui::TextColored(kMedium, "%s", TR("mig_receive_addr"));
|
||||
ImGui::TextWrapped("%s", seed_migration_dest_.c_str());
|
||||
ImGui::Spacing();
|
||||
if (ui::material::StyledButton(TR("mig_copy_seed"), ImVec2(120 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_copy_seed"), ImVec2(120 * dp, 0)))
|
||||
copySecretToClipboard(seed_migration_seed_);
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("seed_backup_save"), ImVec2(130 * dp, 0))) {
|
||||
if (ui::material::TactileButton(TR("seed_backup_save"), ImVec2(130 * dp, 0))) {
|
||||
std::string path = util::Platform::getConfigDir() + "/dragonx-seed-wallet-backup.txt";
|
||||
std::string content = seed_migration_seed_ + "\nAddress: " + seed_migration_dest_ + "\n";
|
||||
const bool ok = util::Platform::writeFileAtomically(path, content, /*restrict=*/true);
|
||||
@@ -3636,9 +3638,8 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::OnSurfaceMedium());
|
||||
ImGui::TextWrapped("%s", TR("mig_step1_done"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Separator();
|
||||
if (!seed_migration_backed_up_) ImGui::BeginDisabled();
|
||||
if (ui::material::StyledButton(TR("mig_continue_sweep"), ImVec2(170 * dp, 0))) {
|
||||
if (ui::material::TactileButton(TR("mig_continue_sweep"), ImVec2(170 * dp, 0))) {
|
||||
wipeSeed(); // the sweep/adopt steps only use the address, never the seed itself
|
||||
seed_migration_step_ = SeedMigrationStep::Sweep;
|
||||
seed_migration_balance_loaded_ = false;
|
||||
@@ -3647,7 +3648,7 @@ void App::renderSeedMigrationDialog()
|
||||
}
|
||||
if (!seed_migration_backed_up_) ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("mig_discard"), ImVec2(160 * dp, 0))) {
|
||||
if (ui::material::TactileButton(TR("mig_discard"), ImVec2(160 * dp, 0))) {
|
||||
// Throw away the isolated new wallet + clear the pending state.
|
||||
if (!seed_migration_temp_dir_.empty()) {
|
||||
std::error_code ec;
|
||||
@@ -3671,8 +3672,8 @@ void App::renderSeedMigrationDialog()
|
||||
// value, so gate on the loaded flag to avoid flashing "no funds" while it's still fetching).
|
||||
if (!seed_migration_balance_loaded_) {
|
||||
ImGui::TextColored(kMedium, "%s%s", TR("mig_checking_balance"), ui::material::LoadingDots());
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("mig_later"), ImVec2(100 * dp, 0))) close();
|
||||
ImGui::Spacing();
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0))) close();
|
||||
break;
|
||||
}
|
||||
if (seed_migration_balance_ <= DRAGONX_DEFAULT_FEE) {
|
||||
@@ -3685,16 +3686,16 @@ void App::renderSeedMigrationDialog()
|
||||
// Adopt swaps wallet.dat — gate it behind an explicit acknowledgement even though no
|
||||
// funds are at risk (the legacy wallet is still moved aside to a backup on adopt).
|
||||
ImGui::Checkbox(TR("mig_nofunds_confirm"), &seed_migration_nofunds_confirmed_);
|
||||
ImGui::Spacing(); ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
if (!seed_migration_nofunds_confirmed_) ImGui::BeginDisabled();
|
||||
if (ui::material::StyledButton(TR("mig_adopt_now"), ImVec2(190 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_adopt_now"), ImVec2(190 * dp, 0)))
|
||||
beginAdoptSeedWallet();
|
||||
if (!seed_migration_nofunds_confirmed_) ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
refreshSeedMigrationBalance();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
close();
|
||||
break;
|
||||
}
|
||||
@@ -3710,19 +3711,18 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
{
|
||||
const bool canSweep = !state_.isLocked() && seed_migration_balance_ > 0.0;
|
||||
if (!canSweep) ImGui::BeginDisabled();
|
||||
if (ui::material::StyledButton(TR("mig_sweep_all"), ImVec2(170 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_sweep_all"), ImVec2(170 * dp, 0)))
|
||||
beginSweepToSeedWallet();
|
||||
if (!canSweep) ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
refreshSeedMigrationBalance();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
close(); // pending state persists; resumes here next time
|
||||
break;
|
||||
}
|
||||
@@ -3749,24 +3749,23 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::TextColored(kMedium, "%s", rbuf);
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
if (confirmed && legacyEmpty) {
|
||||
if (ui::material::StyledButton(TR("mig_adopt"), ImVec2(180 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_adopt"), ImVec2(180 * dp, 0)))
|
||||
beginAdoptSeedWallet();
|
||||
} else if (confirmed && !legacyEmpty) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
||||
ImGui::TextWrapped("%s", TR("mig_remainder"));
|
||||
ImGui::PopStyleColor();
|
||||
if (ui::material::StyledButton(TR("mig_sweep_remaining"), ImVec2(180 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_sweep_remaining"), ImVec2(180 * dp, 0)))
|
||||
beginSweepToSeedWallet();
|
||||
} else {
|
||||
ImGui::TextColored(kMedium, "%s", TR("mig_waiting_mine"));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
pollSweepStatus();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::StyledButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
close(); // pending state + txid persist; resumes here next time
|
||||
break;
|
||||
}
|
||||
@@ -3794,8 +3793,7 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("mig_done_btn"), ImVec2(120 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("mig_done_btn"), ImVec2(120 * dp, 0)))
|
||||
close();
|
||||
break;
|
||||
|
||||
@@ -3804,8 +3802,7 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::TextWrapped("%s", seed_migration_status_.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0)))
|
||||
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0)))
|
||||
close();
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user