build method

Initialize a Notifier.

It is safe to use Ref.watch or Ref.listen inside this method.

If a dependency of this Notifier (when using Ref.watch) changes, then build will be re-executed. On the other hand, the Notifier will not be recreated. Its instance will be preserved between executions of build.

If this method throws, reading this provider will rethrow the error.

Implementation

@override
AutoShieldStatus build() {
  _disposed = false;
  _generation++;
  _deniedThisSession = false;
  _quietAtTransparentZat = null;
  ref.onDispose(() => _disposed = true);

  // Identity keys the loop (same rule as the whole live graph). Everything
  // else is a LISTEN, not a watch — a policy/balance change must evaluate,
  // not rebuild (a rebuild would re-arm a denied latch).
  final session = ref.watch(walletSessionProvider);

  // Every refreshed snapshot is an evaluation point: the wallet-active sync
  // edges (reachedTip / becameSpendable) invalidate it, so arrivals are
  // seen exactly when the balance is. The post-attempt invalidate below
  // re-evaluates once more — by then the proposed UTXOs are consumed and
  // `proposeShield` quietly returns null, so there is no hot loop.
  ref.listen(walletSnapshotProvider, (_, next) {
    if (next.hasValue) _maybeAttempt();
  });
  // Power-save lifting and the switch loading/turning ON are the two other
  // "now it may fire" edges.
  ref.listen(walletPowerSaveActiveProvider, (_, active) {
    if (!active) _maybeAttempt();
  });
  ref.listen(walletAutoShieldEnabledProvider, (prev, next) {
    // TRANSITION-keyed on SETTLED values (wrap security MINOR-3): a
    // provider rebuild re-emits the current value through loading-with-
    // previous states — a value-keyed arm would re-arm a denied latch (and
    // re-prompt a denying host) with no user gesture. Only a settled value
    // that DIFFERS from the previous one is a user-driven flip; the cold
    // load (prev carries no value) counts as a flip in its own direction.
    if (!next.hasValue || next.isLoading) return;
    final on = next.value == true;
    final wasOn = prev?.value == true;
    if (on && !wasOn) {
      // Turning the switch ON is the user's explicit "run automation" —
      // it RE-ARMS a host-denied loop (the denial latch guards against
      // prompt SPAM, not against a deliberate restart; review m6) and
      // re-evaluates.
      _deniedThisSession = false;
      _maybeAttempt();
    } else if (!on && (prev == null || wasOn)) {
      // A deliberate OFF is the expert's hold-transparent choice — a
      // lingering failure/denial cue would contradict the explicit opt-out
      // (review m5); the standing transparent note still explains the
      // visibility.
      _setStatus(AutoShieldStatus.idle, _generation);
    }
  });

  if (session != null) {
    // Cold-start evaluation (a wallet opened with an existing transparent
    // balance must not wait for the first sync edge). Deferred: build()
    // must not write state.
    Future<void>.microtask(_maybeAttempt);
  }
  return AutoShieldStatus.idle;
}