feat(seed): glow the Migrate-to-seed button for a legacy wallet

A pre-seed-phrase (legacy, non-mnemonic) wallet is exactly the one that benefits
from migrating — so nudge the user by glowing the Settings "Migrate to seed"
button with a soft pulsing accent halo.

Adds a cached wallet mnemonic status (probeWalletSeedStatus), classified once per
connect via z_exportmnemonic (the same signal the migration Intro pre-flight
uses): NoMnemonic = legacy → glow; HasMnemonic / Incapable (old daemon) / while
locked or on lite = no glow. Reset on disconnect so it re-probes after a wallet
switch or a post-migration adopt, with a small attempt cap to avoid re-probing on
a persistent transient error. The sweep forces the status so the glow is
captured (restored after). Verified dark + light.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 18:24:41 -05:00
parent 3a9edf2200
commit aa587d1d84
5 changed files with 69 additions and 1 deletions

View File

@@ -579,6 +579,11 @@ void App::onDisconnected(const std::string& reason)
state_.clear();
connection_status_ = reason;
// Re-classify the wallet's mnemonic status on the next connect (the active wallet may have
// changed — a switch or a post-migration adopt both disconnect here).
wallet_seed_status_ = WalletSeedStatus::Unknown;
wallet_seed_status_attempts_ = 0;
// Clear RPC result caches
viewtx_cache_.clear();
confirmed_tx_cache_.clear();
@@ -2936,6 +2941,35 @@ void App::maybeRemindSeedBackup()
});
}
// One-shot (per connect) probe of the current wallet's mnemonic status, so the Settings
// Migrate-to-seed button can glow for a legacy wallet without opening the migration dialog. Same
// classification as the migration Intro pre-flight, but proactive and cached. Reads no secret past
// the exportSeedPhrase callback (which wipes the phrase). Retries next tick on a transient failure.
void App::probeWalletSeedStatus()
{
if (capture_mode_) return; // no live ops during a UI sweep
if (lite_wallet_) return; // lite has its own seed UX
if (wallet_seed_status_ != WalletSeedStatus::Unknown) return; // already classified
if (!state_.connected || !state_.encryption_state_known) return;
if (state_.isLocked()) return; // needs an unlocked wallet to read it
if (wallet_seed_status_in_flight_) return;
wallet_seed_status_in_flight_ = true;
exportSeedPhrase([this](bool ok, bool noMnemonic, const std::string& /*phrase*/,
const std::string& error) {
wallet_seed_status_in_flight_ = false;
if (ok)
wallet_seed_status_ = WalletSeedStatus::HasMnemonic;
else if (noMnemonic)
wallet_seed_status_ = WalletSeedStatus::NoMnemonic; // legacy → migratable → glow
else if (error.find("Method not found") != std::string::npos ||
error.find("-32601") != std::string::npos)
wallet_seed_status_ = WalletSeedStatus::Incapable; // old daemon: can't migrate
else if (++wallet_seed_status_attempts_ >= 3)
wallet_seed_status_ = WalletSeedStatus::Incapable; // give up after a few transient errors
// else: transient error → stay Unknown, retry next tick
});
}
void App::beginCreateSeedWallet()
{
if (seed_migration_in_flight_) return;