fix(native): refetch stale queries on app resume#2492
Conversation
TanStack Query's refetchOnWindowFocus keys off visibilitychange, which Android WebViews don't reliably fire on resume — an app restored from background kept rendering its pre-background query data, so the home Activity widget showed weeks-old transactions. Drive the focusManager from Capacitor's appStateChange so stale queries refetch on resume. Also send the history fetch with cache: 'no-store' (server now sets Cache-Control: no-store too) and drop stale service-worker-cache comments — the SW no longer intercepts API responses.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 11 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughChangesQuery freshness
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant CapacitorApp
participant useNativePlugins
participant focusManager
CapacitorApp->>useNativePlugins: appStateChange(isActive)
useNativePlugins->>focusManager: setFocused(isActive)
useNativePlugins->>CapacitorApp: remove listener during teardown
useNativePlugins->>focusManager: setFocused(undefined) during teardown
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Code-analysis diffPainscore total: 6242.76 → 6243.1 (+0.34) 🆕 New findings (11)
✅ Resolved (11)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/hooks/useNativePlugins.ts`:
- Around line 45-51: Update the effect’s awaited native listener registrations
in useNativePlugins to track whether cleanup has already run; when
App.addListener or any other awaited registration resolves after disposal,
immediately remove the late handle instead of storing it, and ensure cleanup
purges all registered handles while preserving the existing focus, back, and
link handler behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 09093127-8f5e-4b17-9b3b-8f2c099f9411
📒 Files selected for processing (3)
src/config/wagmi.config.tsxsrc/hooks/useNativePlugins.tssrc/hooks/useTransactionHistory.ts
💤 Files with no reviewable changes (1)
- src/config/wagmi.config.tsx
If the effect cleans up while a listener registration is still in flight, the handle resolved after teardown and was never removed. Route every registration through a disposed-aware track() helper.
kushagrasarathe
left a comment
There was a problem hiding this comment.
Reviewed adversarially (external contributor, native lifecycle) — SAFE, approving with conditions.
Uses the canonical TanStack focusManager.setFocused() API to re-run stale mounted queries on app resume — correct choice over hand-rolled invalidation. Verified:
- Not a refetch storm — only mounted + stale queries refire,
staleTimeis 30s everywhere, so a resume within 30s is a no-op; the mounted set on a route is bounded. No money mutation is focus-driven. - Listener leak fixed — the
disposed/track()guard correctly handles the asyncApp.addListenerteardown race. - Web-safe — whole effect is gated behind
if (!isCapacitor()) return; the browservisibilitychangepath is untouched.
Conditions (non-blocking, please confirm):
setFocused(false)on background pinsisFocused()false app-wide, which pauses anyrefetchIntervalpolling (balance, deposit-polling) while backgrounded. For a frozen WebView that's arguably desired — but it's an undocumented side effect; add a one-line comment or confirm intent.- Re-run
@coderabbitai review— the automated review was rate-limited to zero line comments here, and this is external code to a native prod path. - Verify sequencing once #2489 (app-lock) merges — both fire on
appStateChange; confirm no double-trigger / stale-balance flash.
Nit: the teardown setFocused(undefined) can fire a spurious app-wide refetch if the last value was true — harmless (layout rarely unmounts) but pointless.
Problem
The Android app's home-screen Activity widget can show weeks-old transactions while web shows the fresh list for the same account.
TanStack Query's
refetchOnWindowFocusrelies onvisibilitychange, which Android WebViews don't reliably fire when the app resumes from background. An app process kept alive/frozen by the OS resumes with its pre-background in-memory query data and never refetches — there is currently no native lifecycle → query-refetch wiring anywhere in the app.Fix
useNativePlugins: listen to CapacitorappStateChangeand drive TanStack'sfocusManager, so stale queries refetch on every app resume (the canonical TanStack pattern for non-browser hosts). Registered first among the App listeners so a deep-link init failure can't skip it.useTransactionHistory: send the history request withcache: 'no-store'— defense in depth alongside the API-sideCache-Control: no-store(companion peanut-api-ts PR).public/sw.js).Verification
tsc --noEmitclean; prettier run on touched files.Notes
Ships in the next binary/OTA release; no API contract changes.
Summary by CodeRabbit