walletCatchUpCueProvider top-level property

Provider<WalletCatchUpCue> walletCatchUpCueProvider
final

See WalletCatchUpCue. A plain derived Provider: every input is already live state, so this re-evaluates exactly when they change.

Implementation

final walletCatchUpCueProvider = Provider<WalletCatchUpCue>((ref) {
  final rescan = ref.watch(walletRescanControllerProvider);
  if (rescan is WalletRescanRebuilding) {
    return WalletCatchUpRebuilding(target: rescan.target);
  }
  final session = ref.watch(walletSessionProvider);
  if (session == null) return const WalletCatchUpNone();
  // Last-known snapshot (`.value` — same retention idiom as the screen): a
  // transient re-read failure must not flap the cue. No snapshot yet ⇒ no
  // verdict ⇒ no cue (the screen is still on its cold-load spinner anyway).
  final snapshot = ref.watch(walletSnapshotProvider).value;
  if (snapshot == null || snapshot.lastSynced != null) {
    return const WalletCatchUpNone();
  }
  // The cold snapshot's own status carries the first frame before the live
  // stream emits (the screen's `liveStatus ?? state.syncStatus` idiom).
  final status = ref.watch(syncStatusProvider).value ?? snapshot.syncStatus;
  if (status is SyncStatus_UpToDate) return const WalletCatchUpNone();
  // #357 (post-ship reliability/UX HIGH fix): the durable everSynced flag is now
  // CLEARED on a rescan by the core (its aux row is reset like `sync_stamp`), so
  // `everSynced == true` UNAMBIGUOUSLY means "reached tip since the last rescan ⇒
  // the balance is complete" — suppress the over-explanation of a settled balance
  // behind a stale-null stamp (a swallowed stamp-write fault / pre-#317 wallet /
  // offline relaunch), with NO scanning-or-balance heuristic. A rescan REBUILD has
  // everSynced CLEARED, so it falls through and correctly shows the cue for its
  // whole catch-up — INCLUDING offline after a process death (LMK kill), the panic
  // window an earlier `status is Scanning` proxy wrongly suppressed. A never-synced
  // wallet (`everSynced == false`) likewise falls through to the first-run cue.
  if (snapshot.everSynced) return const WalletCatchUpNone();
  // A tip this SESSION already proved stays proved: the
  // stamp's visibility here rides an async snapshot re-read that can fault
  // (busy DB, `.value` retention) or simply not be wired (a host mounting
  // only the swap surface) — without this, a routine post-tip scan or an
  // offline drop would resurrect "catching up" over a FINAL balance. The
  // latch is session-keyed (resets on the rescan's own swap) and held
  // through routine re-scan windows by design, which is exactly the
  // no-flap semantic the cue needs. An INVALIDATED latch (deep-reorg
  // rewind) deliberately falls through: over a never-stamped wallet a
  // rewound re-scan genuinely is a catch-up.
  if (ref.watch(walletSyncedTipProvider) is WalletSyncedTipLatched) {
    return const WalletCatchUpNone();
  }
  // #377 s357b-2: the durable rescan-rebuilding breadcrumb — a rescan swapped
  // in and has not reached tip since (the core clears it at the first
  // post-rescan clean pass). The in-session fast path above already handled a
  // LIVE rescan with its named target; reaching here with the breadcrumb set
  // means the choice was lost (process death mid-rebuild — the LMK-kill
  // window), so name the rescan generically rather than falling through to
  // the first-run framing. Checked AFTER everSynced and the session latch:
  // both prove the rebuild is over, so a stale breadcrumb (a swallowed
  // clear fault at tip) can never pin this banner over a settled balance.
  if (snapshot.rescanRebuilding) {
    return const WalletCatchUpRebuilding(target: null);
  }
  return const WalletCatchUpSyncing();
});