features/wallet/sync_status_presentation library

Classes

SyncStatusPresentation
Pure per-arm presentation of the live SyncStatus — ONE mapping shared by the compact badge row and the sync-detail sheet (the two surfaces show the same state and must never drift on icon/headline/copy). Extracted from the badge (spec §3.3, ADR-0533); the COLOR stays separate — it comes from walletBadgeLevel (the gate-8 truth-tabled health rule), never from here.
TransportPresentation
Pure per-state presentation of HOW wallet traffic reaches the network — ONE mapping shared by the sync-row indicator and the sheet's Connection section (the badge/sheet no-drift rule, same as syncStatusPresentation).
WalletHostTransport
A HOST-provided transport description — for hosts that route wallet traffic through their OWN privacy layer (an app-wide proxy: xray/vless, a VPN tunnel, a custom Tor integration) that the SDK cannot see. The SDK's own TorState then honestly reads "Tor off" even though the traffic IS protected — this override lets the host tell the truth instead. Wired via walletHostTransportProvider (wallet_providers.dart); null (the default) derives the presentation from the SDK's TorState.

Enums

HostDialerSentences
Which SENTENCE FAMILY hostDialerPresentation speaks for the payload it is given. The privacy question — may this path be called private? — has ONE answer per payload and is decided in one place for both; only the words differ, because the two states say different things about carriage.
TransportTone
The indicator tone for a transport state — mapped to theme colors at the render (the same pattern as WalletBadgeLevel): protected green, neutral muted (informational — a direct connection is the configured default, not an error), progress cyan (bootstrapping), caution orange (fell back / unverifiable), danger red (required but unavailable).

Functions

compactBlockCount(int count, String locale) → String
Compact block-count formatter (1,579,873 → "1.6M"), locale-aware (#317: the old locale-DEFAULT static froze to whatever Intl.defaultLocale was at first use — an "1.6M" inside a ru sentence). locale is WalletLocalizations.of(context).localeName, so the digits follow the strings around them. Built once PER LOCALE (a formatter per rebuild is wasteful even at the badge's low update rate; the cache is bounded by the app's locale set).
exactBlockCount(int count, String locale) → String
Exact grouped block count for the DETAIL sheet ("1,579,873") — the sheet is where the big number belongs (the badge deliberately demotes it to compact; the maintainer's "scary jittery number" report). Locale-aware and memoized like compactBlockCount.
graceEndedText(WalletLocalizations l10n, GraceExpiry by, int? blocksSinceLastCurrent) → String
Why the grace ended, as one sentence with the next step — see unknownBranchGraceText. blocksSinceLastCurrent is what the blocks sentence names; a missing count falls to the never-confirmed sentence rather than inventing a number.
hostDialerPresentation(WalletLocalizations l10n, {required String name, required IsolationSupport isolation, required TransportExposure exposure, required HostDialerSentences sentences}) → TransportPresentation
The presentation of TorState.active(runtime: hostDialer(name, isolation, exposure)) (FR-29 spec §3.4, ADR-0547 — the host NAMES its transport; the SDK has no list): the chip carries the host's own name VERBATIM ("via your app's private path (Tor)", "… (VLESS via Cloudflare)"), never a name the wallet chose; an EMPTY name (only the SDK's unattributed rendering produces one) reads as the locale's "a private path". exposure decides privacy: exposed renders "not private (your app's direct connection)" — CAUTION tone since FR-30 (c), the direct-connection explanation — whatever the isolation says; unknown (not declared, or a value THIS binding cannot read) renders linkable with the caution tone and the unverified explanation, whatever the isolation says (the wave review's MEDIUM: never the confident tone for a path the wallet cannot vouch for); hidden renders the private path, and only hidden + isolation supported earns the protected tone (§3.3 privacy rule) — unsupported OR unknown isolation adds "; connections can be linked by the proxy" with the caution tone (the state never promises what the host did not declare).
percentOf(double fraction) → int
0..1 fraction → a rounded whole percent (Tor bootstrap progress, where reaching 100 is not a money claim). The bridge guarantees finite percents; clamp alone would pass a NaN through, so guard finiteness explicitly (belt that actually holds).
poolReportIsDegraded(PoolServiceReport report) → bool
The Dart mirror of report_is_degraded in sdk/zec-wallet-core/src/sync_controller.rs ("is at least one pool in the report refused, lied about, or withheld — the condition under which the report earns its own status") — ONE predicate (§4r U-3), so the surface and the core agree on what "degraded" means. The core publishes upToDateDegraded exactly when this is true of a current server's report and carries the report WHOLE on endpointBehind and upToDateUnverified, degraded or not — so this is what decides whether those arms render pool lines at all, and a report with every pool served renders none.
poolServiceLines(WalletLocalizations l10n, PoolServiceReport report) → List<String>
One detail line per pool _poolServiceIsDegraded counts — the pool named, then how this server failed it ("Sapling: this server refuses to serve it") — and NOTHING for a pool served normally. In the core's field order (Sapling, Orchard, Ironwood: SUBTREE_ROOT_POOLS' wire order). This is the money fact the headline cannot carry (§4m #10): WHICH pool's funds cannot be spent through this server, so "the balance is a floor" has a subject. The pool names are l10n keys of their own (proper nouns a locale may keep or transliterate), never Dart literals.
scanPercent(double fraction) → int
0..1 SCAN fraction → a whole percent that NEVER reads 100 before sync is actually done: round would show "Scanning 100%" at 0.995+ while the wallet is provably not up-to-date (the distinct UpToDate arm is the only honest 100%) — a money-honesty trap (a user trusts a partial balance as final). Floor, and cap at 99 while scanning.
stallReasonText(WalletLocalizations l10n, StallReason reason) → String
syncStatusExplanation(WalletLocalizations l10n, SyncStatus status, {required bool driving, bool startFailed = false, bool syncDisabled = false}) → String
The plain-language sheet explanation of the CURRENT state (what it means, whether the balance is trustworthy, what — if anything — to do). Sheet-only copy; the badge headline stays the compact form above.
syncStatusPresentation(WalletLocalizations l10n, SyncStatus status, {required bool driving, bool startFailed = false, bool syncDisabled = false}) → SyncStatusPresentation
transportPresentation(WalletLocalizations l10n, {required TorState tor, WalletHostTransport? host}) → TransportPresentation
Map the SDK's TorState — or the HOST's own transport claim, which WINS when provided (the host knows about tunnels the SDK cannot see) — to the shared presentation. PRIVACY RULE (spec §3.3): every arm that cannot POSITIVELY verify protection reads as not protected; only a verified Tor runtime or an explicit host protection: true earns the protected tone.
unknownBranchGraceText(WalletLocalizations l10n, UnknownBranchGrace grace) → String
The one sentence for where the grace for a server that does not report its network stands (GRACE-1, §4p Q-G2) — the detail line under SyncStatus.upToDateUnverified, and, for an ended grace, the body of the send fault (SendServerSilentFault) and the parked row's reason, so the three surfaces can never say different things about the same state.
walletCompactTimeFormat(String locale) → DateFormat
The activity/parked rows' compact date+time (MMMd + jm, e.g. "Jun 24, 3:45 PM"), locale-aware and memoized per locale (#317 — the old per-widget statics froze to Intl.defaultLocale, an en-US time inside a ru sentence). ONE factory shared by every row surface so the idiom can't drift.
walletFullDateTimeFormat(String locale) → DateFormat
The detail sheet's FULL date+time (yMMMd + jm — the sheet is where the exact moment belongs; rows keep the compact form). Locale-aware, memoized.
walletSyncPausedQualified(WalletLocalizations l10n, String body, {required bool syncPassesRun}) → String
ONE place that appends the sync-paused qualifier to a money body which promises the wallet will finish something on a later sync (#401 R1b/R5).