feat(ui): large-wallet nudge as a one-time toast + clickable alert

Extend the wallet-bloat warning beyond the Settings banner: when wallet.dat
first crosses 500 MB (full-node, synced), fire a one-time warning toast plus a
clickable "Consolidate notes…" entry in the bell/alerts panel that opens Merge
to Address. The persisted large_wallet_warned flag keeps it once-only and
re-arms if the file later shrinks back under the threshold.

- AlertRecord gains an optional onClick + actionHint; Notifications::action()
  pushes a toast and a clickable history entry. renderAlertHistoryPanel() now
  renders the accent action link (under the message) and measures true content
  height so wrapped messages + the link aren't clipped.
- App::maybeWarnLargeWallet() (mirrors maybeRemindSeedBackup) runs once per
  launch from update(); reuses the existing wallet_size_warn/consolidate strings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-20 18:09:36 -05:00
parent 6d26ccd0ed
commit 5daf2d83b6
6 changed files with 83 additions and 6 deletions

View File

@@ -35,6 +35,7 @@
#include "rpc/connection.h"
#include "chat/chat_identity.h" // deriveChatIdentityFromSecret for HushChat identity provisioning
#include "ui/windows/chat_tab.h" // ui::ResetChatTab — wipe chat UI plaintext on a wallet switch
#include "ui/windows/shield_dialog.h" // ui::ShieldDialog — Merge to Address shortcut from the bloat nudge
#include "ui/windows/mining_pool_panel.h" // ui::resolveMiningUserAddress
#include <sodium.h> // sodium_memzero for wiping the fetched mnemonic
#include <cctype>
@@ -4232,6 +4233,36 @@ void App::maybeRemindSeedBackup()
});
}
// One-time nudge (full-node) when the BDB wallet.dat has bloated past the threshold. Berkeley DB never
// shrinks in place and shielded-note witness data accumulates, so a mining/shielded wallet can grow
// unbounded. Fires ONCE (persisted flag) a warning toast + a clickable "Consolidate notes…" entry in the
// bell/alert panel that opens Merge to Address; re-arms if the file later drops back under the threshold.
void App::maybeWarnLargeWallet()
{
if (capture_mode_ || lite_wallet_) return; // no live nags during a UI sweep; lite has no wallet.dat
if (!supportsFullNodeLifecycleActions() || !settings_) return;
if (!state_.connected || !state_.encryption_state_known) return;
if (state_.warming_up || state_.daemon_initializing || !state_.sync.isSynced()) return;
if (large_wallet_checked_) return; // stat wallet.dat at most once per launch
large_wallet_checked_ = true;
static constexpr uint64_t kWalletBloatWarnBytes = 500ull * 1024 * 1024; // 500 MB (matches the Settings banner)
const std::string walletPath = util::Platform::getDragonXDataDir() + "/wallet.dat";
const uint64_t sz = util::Platform::getFileSize(walletPath);
if (sz <= kWalletBloatWarnBytes) {
// Re-arm the one-time warning if the file shrank back under the threshold (e.g. after a fresh seed wallet).
if (settings_->getLargeWalletWarned()) { settings_->setLargeWalletWarned(false); settings_->save(); }
return;
}
if (settings_->getLargeWalletWarned()) return; // already warned once for this bloat episode
settings_->setLargeWalletWarned(true);
settings_->save();
ui::Notifications::instance().action(
TR("wallet_size_warn"), ui::NotificationType::Warning,
[]() { ui::ShieldDialog::show(ui::ShieldDialog::Mode::MergeToAddress); },
TR("wallet_size_consolidate"), 12.0f);
}
// Complementary on-disk safety net (full-node) for a wallet salvage we did NOT witness this launch — it
// happened on a prior run, or under an external daemon whose startup output we never captured, so
// detectWalletAutoRecovery() never fired. If the active wallet loads EMPTY while a sibling wallet file in