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::Sweeping
|
||||||
|| seed_migration_step_ == SeedMigrationStep::Adopting;
|
|| 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();
|
if (!busy) wipeSeed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3544,14 +3548,14 @@ void App::renderSeedMigrationDialog()
|
|||||||
// probe found so we never offer a pointless (already-seeded) or impossible (old daemon) run.
|
// probe found so we never offer a pointless (already-seeded) or impossible (old daemon) run.
|
||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
ImGui::TextWrapped("%s", TR("mig_not_connected"));
|
ImGui::TextWrapped("%s", TR("mig_not_connected"));
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (state_.isLocked()) {
|
if (state_.isLocked()) {
|
||||||
ImGui::TextWrapped("%s", TR("mig_unlock_to_migrate"));
|
ImGui::TextWrapped("%s", TR("mig_unlock_to_migrate"));
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!seed_migration_precheck_started_ && !capture_mode_) beginSeedMigrationPrecheck();
|
if (!seed_migration_precheck_started_ && !capture_mode_) beginSeedMigrationPrecheck();
|
||||||
@@ -3564,37 +3568,37 @@ void App::renderSeedMigrationDialog()
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::SuccessVec4());
|
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::SuccessVec4());
|
||||||
ImGui::TextWrapped("%s", TR("mig_already_mnemonic"));
|
ImGui::TextWrapped("%s", TR("mig_already_mnemonic"));
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (ui::material::StyledButton(TR("mig_backup_instead"), ImVec2(200 * dp, 0))) {
|
if (ui::material::TactileButton(TR("mig_backup_instead"), ImVec2(200 * dp, 0))) {
|
||||||
close();
|
close();
|
||||||
showSeedBackupDialog();
|
showSeedBackupDialog();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
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;
|
break;
|
||||||
case SeedMigrationPrecheck::DaemonTooOld:
|
case SeedMigrationPrecheck::DaemonTooOld:
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
||||||
ImGui::TextWrapped("%s", TR("mig_daemon_too_old"));
|
ImGui::TextWrapped("%s", TR("mig_daemon_too_old"));
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0))) close();
|
||||||
break;
|
break;
|
||||||
case SeedMigrationPrecheck::CheckFailed:
|
case SeedMigrationPrecheck::CheckFailed:
|
||||||
ImGui::TextWrapped("%s", TR("mig_check_failed"));
|
ImGui::TextWrapped("%s", TR("mig_check_failed"));
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (ui::material::StyledButton(TR("mig_recheck"), ImVec2(120 * dp, 0)))
|
if (ui::material::TactileButton(TR("mig_recheck"), ImVec2(120 * dp, 0)))
|
||||||
seed_migration_precheck_started_ = false; // re-probe next frame
|
seed_migration_precheck_started_ = false; // re-probe next frame
|
||||||
ImGui::SameLine();
|
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;
|
break;
|
||||||
case SeedMigrationPrecheck::Legacy:
|
case SeedMigrationPrecheck::Legacy:
|
||||||
default:
|
default:
|
||||||
ImGui::TextWrapped("%s", TR("mig_intro"));
|
ImGui::TextWrapped("%s", TR("mig_intro"));
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (ui::material::StyledButton(TR("mig_create"), ImVec2(180 * dp, 0)))
|
if (ui::material::TactileButton(TR("mig_create"), ImVec2(180 * dp, 0)))
|
||||||
beginCreateSeedWallet();
|
beginCreateSeedWallet();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::StyledButton(TR("cancel"), ImVec2(120 * dp, 0)))
|
if (ui::material::TactileButton(TR("cancel"), ImVec2(120 * dp, 0)))
|
||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3608,19 +3612,17 @@ void App::renderSeedMigrationDialog()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SeedMigrationStep::ShowSeed: {
|
case SeedMigrationStep::ShowSeed: {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
ui::material::DialogWarningHeader(TR("mig_seed_warning"));
|
||||||
ImGui::TextWrapped("%s", TR("mig_seed_warning"));
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ui::RenderSeedWordGrid(ui::SplitSeedWords(seed_migration_seed_));
|
ui::RenderSeedWordGrid(ui::SplitSeedWords(seed_migration_seed_));
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::TextColored(kMedium, "%s", TR("mig_receive_addr"));
|
ImGui::TextColored(kMedium, "%s", TR("mig_receive_addr"));
|
||||||
ImGui::TextWrapped("%s", seed_migration_dest_.c_str());
|
ImGui::TextWrapped("%s", seed_migration_dest_.c_str());
|
||||||
ImGui::Spacing();
|
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_);
|
copySecretToClipboard(seed_migration_seed_);
|
||||||
ImGui::SameLine();
|
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 path = util::Platform::getConfigDir() + "/dragonx-seed-wallet-backup.txt";
|
||||||
std::string content = seed_migration_seed_ + "\nAddress: " + seed_migration_dest_ + "\n";
|
std::string content = seed_migration_seed_ + "\nAddress: " + seed_migration_dest_ + "\n";
|
||||||
const bool ok = util::Platform::writeFileAtomically(path, content, /*restrict=*/true);
|
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::PushStyleColor(ImGuiCol_Text, ui::material::OnSurfaceMedium());
|
||||||
ImGui::TextWrapped("%s", TR("mig_step1_done"));
|
ImGui::TextWrapped("%s", TR("mig_step1_done"));
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::Separator();
|
|
||||||
if (!seed_migration_backed_up_) ImGui::BeginDisabled();
|
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
|
wipeSeed(); // the sweep/adopt steps only use the address, never the seed itself
|
||||||
seed_migration_step_ = SeedMigrationStep::Sweep;
|
seed_migration_step_ = SeedMigrationStep::Sweep;
|
||||||
seed_migration_balance_loaded_ = false;
|
seed_migration_balance_loaded_ = false;
|
||||||
@@ -3647,7 +3648,7 @@ void App::renderSeedMigrationDialog()
|
|||||||
}
|
}
|
||||||
if (!seed_migration_backed_up_) ImGui::EndDisabled();
|
if (!seed_migration_backed_up_) ImGui::EndDisabled();
|
||||||
ImGui::SameLine();
|
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.
|
// Throw away the isolated new wallet + clear the pending state.
|
||||||
if (!seed_migration_temp_dir_.empty()) {
|
if (!seed_migration_temp_dir_.empty()) {
|
||||||
std::error_code ec;
|
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).
|
// value, so gate on the loaded flag to avoid flashing "no funds" while it's still fetching).
|
||||||
if (!seed_migration_balance_loaded_) {
|
if (!seed_migration_balance_loaded_) {
|
||||||
ImGui::TextColored(kMedium, "%s%s", TR("mig_checking_balance"), ui::material::LoadingDots());
|
ImGui::TextColored(kMedium, "%s%s", TR("mig_checking_balance"), ui::material::LoadingDots());
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (ui::material::StyledButton(TR("mig_later"), ImVec2(100 * dp, 0))) close();
|
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0))) close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (seed_migration_balance_ <= DRAGONX_DEFAULT_FEE) {
|
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
|
// 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).
|
// 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::Checkbox(TR("mig_nofunds_confirm"), &seed_migration_nofunds_confirmed_);
|
||||||
ImGui::Spacing(); ImGui::Separator();
|
ImGui::Spacing();
|
||||||
if (!seed_migration_nofunds_confirmed_) ImGui::BeginDisabled();
|
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();
|
beginAdoptSeedWallet();
|
||||||
if (!seed_migration_nofunds_confirmed_) ImGui::EndDisabled();
|
if (!seed_migration_nofunds_confirmed_) ImGui::EndDisabled();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::StyledButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||||
refreshSeedMigrationBalance();
|
refreshSeedMigrationBalance();
|
||||||
ImGui::SameLine();
|
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();
|
close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3710,19 +3711,18 @@ void App::renderSeedMigrationDialog()
|
|||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
|
||||||
{
|
{
|
||||||
const bool canSweep = !state_.isLocked() && seed_migration_balance_ > 0.0;
|
const bool canSweep = !state_.isLocked() && seed_migration_balance_ > 0.0;
|
||||||
if (!canSweep) ImGui::BeginDisabled();
|
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();
|
beginSweepToSeedWallet();
|
||||||
if (!canSweep) ImGui::EndDisabled();
|
if (!canSweep) ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::StyledButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||||
refreshSeedMigrationBalance();
|
refreshSeedMigrationBalance();
|
||||||
ImGui::SameLine();
|
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
|
close(); // pending state persists; resumes here next time
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3749,24 +3749,23 @@ void App::renderSeedMigrationDialog()
|
|||||||
ImGui::TextColored(kMedium, "%s", rbuf);
|
ImGui::TextColored(kMedium, "%s", rbuf);
|
||||||
}
|
}
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
|
||||||
if (confirmed && legacyEmpty) {
|
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();
|
beginAdoptSeedWallet();
|
||||||
} else if (confirmed && !legacyEmpty) {
|
} else if (confirmed && !legacyEmpty) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
||||||
ImGui::TextWrapped("%s", TR("mig_remainder"));
|
ImGui::TextWrapped("%s", TR("mig_remainder"));
|
||||||
ImGui::PopStyleColor();
|
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();
|
beginSweepToSeedWallet();
|
||||||
} else {
|
} else {
|
||||||
ImGui::TextColored(kMedium, "%s", TR("mig_waiting_mine"));
|
ImGui::TextColored(kMedium, "%s", TR("mig_waiting_mine"));
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::StyledButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||||
pollSweepStatus();
|
pollSweepStatus();
|
||||||
ImGui::SameLine();
|
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
|
close(); // pending state + txid persist; resumes here next time
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3794,8 +3793,7 @@ void App::renderSeedMigrationDialog()
|
|||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
if (ui::material::TactileButton(TR("mig_done_btn"), ImVec2(120 * dp, 0)))
|
||||||
if (ui::material::StyledButton(TR("mig_done_btn"), ImVec2(120 * dp, 0)))
|
|
||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -3804,8 +3802,7 @@ void App::renderSeedMigrationDialog()
|
|||||||
ImGui::TextWrapped("%s", seed_migration_status_.c_str());
|
ImGui::TextWrapped("%s", seed_migration_status_.c_str());
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
if (ui::material::TactileButton(TR("close"), ImVec2(120 * dp, 0)))
|
||||||
if (ui::material::StyledButton(TR("close"), ImVec2(120 * dp, 0)))
|
|
||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user