fix: restore analysis on reload instead of rendering a blank page - #205
Conversation
The analytical model lived only in React state, so any fresh page load discarded it. Five pages then returned null and rendered nothing between the navbar and the footer, and /network threw "Cannot read properties of null (reading 'allRepos')". Cache the analysis in IndexedDB with a one-hour TTL, matching the cache the landing page already advertises, and restore it on startup so a reload, bookmark or shared link costs no API calls. IndexedDB rather than localStorage because a single model can run to several megabytes. Add a RequireAnalysis route guard for the case where there is genuinely nothing to restore: send people to the organization picker, which is the only place an analysis can be started. It waits for the cache read so a reload is not redirected away a moment before its own data arrives, and keeps pages mounted while explore() refetches so their skeletons show. Also add the missing null guard to NetworkPage so it is safe on its own, matching the other data pages.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
WalkthroughThe app now persists the latest analysis in IndexedDB, restores it during startup, exposes hydration state, and guards analysis routes. Tests cover restoration, loading, missing-model, and rendered-model states. ChangesAnalysis state flow
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant AppProvider
participant IndexedDB
participant RequireAnalysis
participant AnalysisRoute
Browser->>AppProvider: start application
AppProvider->>IndexedDB: loadAnalysis()
IndexedDB-->>AppProvider: cached analysis or null
AppProvider->>RequireAnalysis: expose model, loading, hydrating
RequireAnalysis->>AnalysisRoute: render when analysis is available
RequireAnalysis-->>Browser: redirect to / when no model and not loading
Suggested labels: Suggested reviewers: Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Addressed Issues:
Fixes #204
What was wrong
The analytical model lived only in React state. Any fresh page load — a reload, a bookmark, a shared link — started with
model === null, and nothing restored it. Two different failures followed:/overview,/repositories,/contributors,/analytics,/governanceif (!model) return null→ blank page between navbar and footer/networkTypeError: Cannot read properties of null (reading 'allRepos')atNetworkPage.jsx:257NetworkPagewas simply missing the null guard its five sibling pages already had.Reproducible on
maintoday:npm run dev, then openhttp://localhost:5173/overviewdirectly —<main>renders with 0 children.The fix
Two parts, matching the approach suggested in #204:
1. Persist and restore the analysis (
src/services/cache.js)Cache the model in IndexedDB with a one-hour TTL — the same "1HR intelligent cache" the landing page already advertises — and restore it on startup. A reload now costs zero API calls, which matters against a 60 req/hr unauthenticated budget.
IndexedDB rather than
localStoragebecause a single model runs to several megabytes for a large org (239 repos for one the size of Vercel), well past the ~5MB quota. Every cache operation is best-effort, so a private window or a browser with storage disabled degrades to the old behaviour rather than breaking.Audit and pull-request results are cached too — they are the most expensive data to refetch.
2. Guard the routes that need a model (
src/components/RequireAnalysis.jsx)For the case where there is genuinely nothing to restore (no cache, or an expired one), redirect to the organization picker — the only place an analysis can be started — instead of rendering an empty page.
Two details worth review:
hydrating, so a reload is not redirected away a fraction of a second before its own cached data arrives.explore()refetches, becauseexplore()clears the model first and each page shows its own skeleton during that window./settingsand/support-usare deliberately left unguarded — Settings is where a PAT is added, so it has to work with no analysis loaded.One subtlety handled: the write-back skips the save that would immediately follow a restore. Without that, every page load would stamp a fresh
savedAtand the entry would never reach its TTL.Verification
npm testRequireAnalysis)npm run build/networkwith no analysisTypeErrors/overviewwith no analysis<main>Verified in a real browser against real IndexedDB, seeding the cache directly so the restore path could be exercised without spending API quota.
Additional Notes:
OverviewPage's language-distribution list logsEach child in a list should have a unique "key" prop. Left alone to keep this diff focused — happy to fix separately.Checklist
AI assistance was used to diagnose and implement this change. Both failure modes were reproduced on a clean
maincheckout before any code was written, the fix was verified in a real browser against real IndexedDB (including TTL expiry and the no-API-calls claim), and the full build and test suite were run before submitting. New unit tests cover all four states of the route guard.Summary by CodeRabbit
New Features
Bug Fixes