fix(lite): welcome "Restore from seed" now prompts for the seed inline
On first run the lite welcome screen's "Restore from seed" button only showed a hint toast and bounced the user to Settings, dismissing the welcome with no wallet open — it never prompted for a seed. Add a real restore step to the welcome wizard: a seed-phrase field + optional birthday height, which calls beginRestoreWalletAsync() (same server failover as create/open), shows "Restoring…" progress, then completes (wallet syncs) or surfaces the error to retry. The seed buffer is wiped on success/Back and in finish(). (The Settings -> Lite -> Restore path already prompted for a seed; this fixes the first-run welcome path.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
85
src/app.cpp
85
src/app.cpp
@@ -2022,6 +2022,11 @@ void App::renderLiteFirstRunPrompt()
|
|||||||
static int progress = 0; // # words confirmed in order
|
static int progress = 0; // # words confirmed in order
|
||||||
static double wrongFlashUntil = 0.0; // brief "not the next word" hint
|
static double wrongFlashUntil = 0.0; // brief "not the next word" hint
|
||||||
static bool creating = false; // async create (with failover) in flight
|
static bool creating = false; // async create (with failover) in flight
|
||||||
|
// Restore-from-seed step (step 3). The seed buffer is SECRET — wiped in finish() and on Back.
|
||||||
|
static char restoreSeed[512] = {};
|
||||||
|
static int restoreBirthday = 0;
|
||||||
|
static bool restoring = false; // async restore in flight
|
||||||
|
static std::string restoreErr;
|
||||||
|
|
||||||
// The welcome page is only relevant before any wallet exists; once create has started
|
// The welcome page is only relevant before any wallet exists; once create has started
|
||||||
// (creating) or reached reveal/verify (step>0), keep showing it through to completion even
|
// (creating) or reached reveal/verify (step>0), keep showing it through to completion even
|
||||||
@@ -2035,6 +2040,10 @@ void App::renderLiteFirstRunPrompt()
|
|||||||
|
|
||||||
auto finish = [&]() {
|
auto finish = [&]() {
|
||||||
wallet::secureWipeLiteSecret(seed);
|
wallet::secureWipeLiteSecret(seed);
|
||||||
|
sodium_memzero(restoreSeed, sizeof(restoreSeed));
|
||||||
|
restoreBirthday = 0;
|
||||||
|
restoring = false;
|
||||||
|
restoreErr.clear();
|
||||||
words.clear();
|
words.clear();
|
||||||
chips.clear();
|
chips.clear();
|
||||||
progress = 0;
|
progress = 0;
|
||||||
@@ -2105,9 +2114,7 @@ void App::renderLiteFirstRunPrompt()
|
|||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button(TR("lite_welcome_restore"), ImVec2(btnW, 0))) {
|
if (ImGui::Button(TR("lite_welcome_restore"), ImVec2(btnW, 0))) {
|
||||||
ui::Notifications::instance().info(TR("lite_welcome_restore_hint"), 8.0f);
|
step = 3; // inline seed-entry restore (see step 3 below)
|
||||||
setCurrentPage(ui::NavPage::Settings);
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
if (ImGui::Button(TR("lite_welcome_later"),
|
if (ImGui::Button(TR("lite_welcome_later"),
|
||||||
@@ -2160,7 +2167,7 @@ void App::renderLiteFirstRunPrompt()
|
|||||||
ui::Notifications::instance().success(TR("lite_welcome_created"), 6.0f);
|
ui::Notifications::instance().success(TR("lite_welcome_created"), 6.0f);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
} else { // step == 2
|
} else if (step == 2) {
|
||||||
// ── Verify: tap the words in order ──────────────────────────────────────
|
// ── Verify: tap the words in order ──────────────────────────────────────
|
||||||
ImGui::PushFont(ui::material::Type().subtitle1());
|
ImGui::PushFont(ui::material::Type().subtitle1());
|
||||||
ImGui::TextUnformatted("Confirm your backup");
|
ImGui::TextUnformatted("Confirm your backup");
|
||||||
@@ -2213,6 +2220,76 @@ void App::renderLiteFirstRunPrompt()
|
|||||||
ui::Notifications::instance().success(TR("lite_welcome_created"), 6.0f);
|
ui::Notifications::instance().success(TR("lite_welcome_created"), 6.0f);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
} else if (step == 3) {
|
||||||
|
// ── Restore from an existing seed phrase ────────────────────────────────
|
||||||
|
ImGui::PushFont(ui::material::Type().subtitle1());
|
||||||
|
ImGui::TextUnformatted(TR("lite_restore_title"));
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + 380.0f);
|
||||||
|
ImGui::TextUnformatted(TR("lite_restore_intro"));
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
if (restoring) {
|
||||||
|
// Async restore (with server failover) in flight — driven by pumpAsyncOpen().
|
||||||
|
ImGui::TextUnformatted("Restoring your wallet\xE2\x80\xA6");
|
||||||
|
if (lite_wallet_->walletOpen()) {
|
||||||
|
ui::Notifications::instance().success(TR("lite_restore_ok"), 6.0f);
|
||||||
|
finish(); // wipes restoreSeed; the wallet then syncs from the lite server
|
||||||
|
} else if (!lite_wallet_->openInProgress() &&
|
||||||
|
!lite_wallet_->lastOpenError().empty()) {
|
||||||
|
restoreErr = lite_wallet_->lastOpenError();
|
||||||
|
restoring = false; // back to the form so the user can fix and retry
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::TextUnformatted(TR("lite_restore_seed_label"));
|
||||||
|
ImGui::InputTextMultiline("##LiteRestoreSeed", restoreSeed, sizeof(restoreSeed),
|
||||||
|
ImVec2(380.0f, ImGui::GetTextLineHeight() * 3.2f));
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextUnformatted(TR("lite_restore_birthday_label"));
|
||||||
|
ImGui::SetNextItemWidth(160.0f);
|
||||||
|
ImGui::InputInt("##LiteRestoreBirthday", &restoreBirthday);
|
||||||
|
if (restoreBirthday < 0) restoreBirthday = 0;
|
||||||
|
|
||||||
|
if (!restoreErr.empty()) {
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::Error());
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + 380.0f);
|
||||||
|
ImGui::TextUnformatted(restoreErr.c_str());
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
ImGui::Spacing(); ImGui::Spacing();
|
||||||
|
|
||||||
|
// Trim surrounding whitespace from the entered seed.
|
||||||
|
std::string seedTrim(restoreSeed);
|
||||||
|
while (!seedTrim.empty() && std::isspace((unsigned char)seedTrim.front())) seedTrim.erase(seedTrim.begin());
|
||||||
|
while (!seedTrim.empty() && std::isspace((unsigned char)seedTrim.back())) seedTrim.pop_back();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(seedTrim.empty());
|
||||||
|
if (ImGui::Button(TR("lite_restore_btn"), ImVec2(btnW, 0))) {
|
||||||
|
wallet::LiteWalletRestoreRequest req;
|
||||||
|
req.seedPhrase = seedTrim;
|
||||||
|
req.birthday = static_cast<unsigned long long>(std::max(0, restoreBirthday));
|
||||||
|
req.overwrite = lite_wallet_->walletExists(); // replace any existing wallet file
|
||||||
|
if (lite_wallet_->beginRestoreWalletAsync(std::move(req))) {
|
||||||
|
restoreErr.clear();
|
||||||
|
restoring = true;
|
||||||
|
} else {
|
||||||
|
restoreErr = lite_wallet_->lastOpenError().empty()
|
||||||
|
? std::string("Could not start restore")
|
||||||
|
: lite_wallet_->lastOpenError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Back", ImVec2(80, 0))) {
|
||||||
|
sodium_memzero(restoreSeed, sizeof(restoreSeed));
|
||||||
|
restoreErr.clear();
|
||||||
|
step = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -655,6 +655,12 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["lite_welcome_created"] = "Wallet created — back up your recovery phrase now in Settings → Backup & keys";
|
strings_["lite_welcome_created"] = "Wallet created — back up your recovery phrase now in Settings → Backup & keys";
|
||||||
strings_["lite_welcome_restore_hint"] = "Restore your wallet under Settings → Lite wallet request";
|
strings_["lite_welcome_restore_hint"] = "Restore your wallet under Settings → Lite wallet request";
|
||||||
strings_["lite_welcome_create_failed"] = "Could not create wallet";
|
strings_["lite_welcome_create_failed"] = "Could not create wallet";
|
||||||
|
strings_["lite_restore_title"] = "Restore from seed phrase";
|
||||||
|
strings_["lite_restore_intro"] = "Enter your recovery seed phrase. The wallet will be restored and then synced from the lite server.";
|
||||||
|
strings_["lite_restore_seed_label"] = "Seed phrase (words separated by spaces)";
|
||||||
|
strings_["lite_restore_birthday_label"] = "Birthday block (optional — 0 scans from the start, slower)";
|
||||||
|
strings_["lite_restore_btn"] = "Restore wallet";
|
||||||
|
strings_["lite_restore_ok"] = "Wallet restored — syncing from the lite server…";
|
||||||
// Lite send-time unlock prompt.
|
// Lite send-time unlock prompt.
|
||||||
strings_["lite_unlock_title"] = "Unlock wallet";
|
strings_["lite_unlock_title"] = "Unlock wallet";
|
||||||
strings_["lite_unlock_msg"] = "Enter your passphrase to unlock the wallet for spending.";
|
strings_["lite_unlock_msg"] = "Enter your passphrase to unlock the wallet for spending.";
|
||||||
|
|||||||
Reference in New Issue
Block a user