feat(lite): M2b-3 — background refresh worker + App::update hook

- LiteWalletController owns a background std::thread worker that, once a wallet is ready,
  refreshes every ~4s and publishes a copyable LiteWalletAppRefreshModel under a mutex.
  Worker auto-starts on lifecycle-ready and is stopped+joined in the destructor. status_
  is written only on the main thread; walletOpen_/syncStarted_ are atomic.
- App::update() calls takeRefreshedModel() and applies it into state_ on the main thread
  (WalletState is non-copyable, so the model crosses the thread boundary, not the state),
  so the existing Balance/Receive/Transactions tabs populate from lite data.
- refreshWalletState() refactored onto refreshModel() (pure, worker-safe).
- testLiteWalletControllerWorkerProducesModel verifies the worker publishes a populated
  model (stable across repeated runs). Builds clean in all configs.

Real-backend smoke (lite_smoke --refresh now runs real output through the parsers) found
two integration bugs, documented in the plan for follow-up:
- syncstatus parser requires synced_blocks/total_blocks but the real idle response is
  {"syncing":"false"} (string), so it fails to parse when not actively syncing.
- the first data query (balance/list) blocks on a full chain sync, which would hang the
  worker's shutdown join — needs a cancel/timeout path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 22:38:34 -05:00
parent 012341b1a4
commit a8b5d2f6a3
7 changed files with 190 additions and 15 deletions

View File

@@ -390,6 +390,14 @@ void App::update()
if (fast_worker_) {
fast_worker_->drainResults();
}
// Apply any lite-wallet refresh the controller's background worker produced (main thread).
if (lite_wallet_) {
wallet::LiteWalletAppRefreshModel liteModel;
if (lite_wallet_->takeRefreshedModel(liteModel)) {
wallet::applyLiteRefreshModelToWalletState(liteModel, state_);
}
}
async_tasks_.reapCompleted();
// Auto-lock check (only when connected + encrypted + unlocked)