fix(flutter): non-blocking version check + 1h staleness throttle
Cold-start spinner was up to ~38s on slow / remote connections because VersionGate blocked the entire ShellRoute on /healthz, with the default dio's 8s connect + 30s receive timeouts. The /healthz server handler itself is fine (microsecond JSON encode); the blocker was client-side. Three issues fixed in one pass: 1. Optimistic render. VersionGate becomes a ConsumerStatefulWidget that always renders its child and just activates the version check controller on mount. The "you're too old" experience moves from a full-screen hard-block (_TooOldScreen, deleted) to a soft banner above the AppBar that lets the user keep playing cached content while they update. 2. 1h-throttled background check. New VersionCheckController (AsyncNotifier) hydrates from a secure-storage cache on boot, returning the cached result instantly. If the cache is missing or >1h old, fires a background recheck. AppLifecycleState.resumed triggers recheckIfStale so foregrounding after >1h re-checks without per-frame hammering. "Check now" button on the banner bypasses the staleness gate so dev iteration (push new APK, want to see banner clear) doesn't wait an hour. 3. Bounded health-check dio. The /healthz request uses a dedicated dio with connectTimeout: 3s + receiveTimeout: 2s rather than the default 8s / 30s. Health probes should fail fast — if the server can't ack in 5s, the user has bigger problems than a stale min_client_version and the cached value remains in effect. Cache keys live alongside the existing tz cadence cache in flutter_secure_storage (kResult + kAtMs). On any network error or parse failure, _runCheck soft-fails without bumping the timestamp, so the next staleness check will retry. VersionTooOldBanner renders in _ShellWithPlayerBar's Column above the existing UpdateBanner — the two coexist when both apply (server rejects you AND an APK is queued). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -151,6 +151,11 @@ class _ShellWithPlayerBar extends ConsumerWidget {
|
||||
final hasBanner = ref.watch(shouldShowUpdateBannerProvider) != null;
|
||||
return Column(
|
||||
children: [
|
||||
// VersionTooOldBanner above UpdateBanner: a server-rejects-you
|
||||
// soft warning carries more user-relevant urgency than the APK
|
||||
// download prompt below it. The two banners can coexist
|
||||
// (server says you're too old AND a local APK is queued).
|
||||
const VersionTooOldBanner(),
|
||||
const UpdateBanner(),
|
||||
Expanded(
|
||||
child: hasBanner
|
||||
|
||||
Reference in New Issue
Block a user