perf: optimize web data resources
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# Audit and optimize web performance
|
||||
|
||||
## Goal
|
||||
|
||||
Comprehensively audit and refactor the web frontend data interaction layer while preserving the existing UI. The refactor should reduce repeated HTTP requests, hide US-backend latency where safe, centralize cache/invalidation behavior, and make authenticated page transitions feel faster in both perceived and actual request-count terms.
|
||||
|
||||
## What I already know
|
||||
|
||||
* Backend is hosted in the US, so users far away experience noticeable request latency.
|
||||
* User has observed repeated HTTP requests and avoidable data-loading waits.
|
||||
* Web stack is Astro 6 + React 19 + React Router DOM under `web/`.
|
||||
* Auth state lives in `web/src/lib/auth.ts`; business APIs live in `web/src/lib/api.ts`.
|
||||
* Existing `getPointsBalance()` has a 1-minute in-memory TTL cache, but many other stable reads do not.
|
||||
* Early code scan found repeated `getUserProfile()` calls across `AppShell`, `LoginForm`, `SettingsPage`, `GeneralSettingsPage`, and `ProfileDetailPage`.
|
||||
* `AppShell` already loads profile globally and exposes `UserSettingsContext`; some pages still refetch profile instead of using that context.
|
||||
* Auth refresh now has single-flight behavior from the previous fix, which helps avoid concurrent refresh waste.
|
||||
|
||||
## Assumptions
|
||||
|
||||
* Performance improvements should preserve backend source of truth and auth safety.
|
||||
* Cached authenticated data must be invalidated after writes that change it.
|
||||
* We should prefer a local, typed data-client/resource layer over adding a large state/query dependency unless audit shows the custom approach is too risky.
|
||||
* Optimizations should be observable through request count reduction and improved perceived loading behavior.
|
||||
|
||||
## Requirements
|
||||
|
||||
* Audit all authenticated web pages for duplicate or avoidable requests.
|
||||
* Define and implement a frontend data layer with caching, in-flight dedupe, stale-while-revalidate, prefetch, and explicit invalidation.
|
||||
* Reuse AppShell-loaded profile/settings where possible instead of refetching.
|
||||
* Add cache invalidation for profile/settings/points changes.
|
||||
* Keep UI presentation unchanged at rest; only data source and loading/refresh behavior should change.
|
||||
* Avoid stale or unsafe auth behavior: 401 and refresh failures must still clear auth and redirect.
|
||||
* Improve perceived performance with cached data, route-level reuse, and fewer full-page loading waits.
|
||||
* Document verification with before/after request counts for key flows.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
* [x] Request audit lists endpoints, call sites, duplicate patterns, and priority.
|
||||
* [x] Refactor plan defines target data architecture, resource cache policies, invalidation rules, and phased rollout.
|
||||
* [x] Data client/resource layer is implemented without changing the visible UI design.
|
||||
* [x] Dashboard → settings/profile/general/divination navigation avoids duplicate profile requests where safe.
|
||||
* [x] Points balance requests are deduped in-flight and cached/invalidated intentionally.
|
||||
* [x] Stable package/config-style reads are cached or intentionally left uncached with rationale.
|
||||
* [x] Authenticated pages keep correct behavior after writes and after 401 responses.
|
||||
* [ ] Browser verification captures representative flows under mobile and desktop viewports.
|
||||
* [x] `git diff --check` passes; build/typecheck status documented.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
* PRD updated with final implementation notes.
|
||||
* Targeted code changes committed.
|
||||
* Performance audit and refactor plan recorded in the task directory.
|
||||
* Any reusable conventions captured in `.trellis/spec/web/index.md` or relevant spec.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
* Backend endpoint redesign.
|
||||
* CDN/edge deployment changes.
|
||||
* Offline mode.
|
||||
* Large dependency adoption unless explicitly approved after audit.
|
||||
* Visual redesign of existing pages.
|
||||
|
||||
## Technical Notes
|
||||
|
||||
* Follow `.trellis/spec/web/index.md`.
|
||||
* All API paths remain in `web/src/lib/api-routes.ts`.
|
||||
* Components should call typed API helpers from `web/src/lib/api.ts`, not inline `fetch('/api/...')`.
|
||||
* Target refactor plan: `.trellis/tasks/05-10-audit-and-optimize-web-performance/refactor-plan.md`.
|
||||
* Request audit: `.trellis/tasks/05-10-audit-and-optimize-web-performance/request-audit.md`.
|
||||
* Current build is blocked by existing Astro adapter configuration (`NoAdapterInstalled`) and should be documented unless fixed in a separate task.
|
||||
|
||||
## Implementation Notes - 2026-05-10
|
||||
|
||||
* Added `web/src/lib/data-client.ts`, a typed in-memory query cache with TTL, in-flight request dedupe, `peek`, `set`, prefix `invalidate`, `prefetch`, `subscribe`, and `clearAll`.
|
||||
* Added `web/src/lib/resources.ts` for profile, points, packages, history list/thread/summary, notifications list, and unread-count cache policy plus React resource hooks.
|
||||
* Moved points TTL behavior out of `api.ts`; `getPointsBalance()` is now transport-only and points caching lives in the resource layer.
|
||||
* AppShell now loads profile through the profile resource, subscribes to profile cache changes, clears data cache via `clearAuth()`, prefetches cheap dashboard data after auth, and prefetches route resources on nav hover/focus.
|
||||
* Settings, general settings, profile detail, dashboard, store, history list, notifications, manual/auto divination, divination processing, result, and follow-up pages now reuse resource caches instead of owning duplicate GET lifecycles.
|
||||
* Mutations patch or invalidate shared cache: profile/settings/avatar set profile; notification read/all-read patch list and unread count; divination/follow-up completion invalidates points and history.
|
||||
* Quality check fixed two post-implementation issues: invalidated active resources now refetch instead of staying empty/stale, and `useHistoryThread(undefined)` no longer emits a doomed request when result pages rely on router/session state.
|
||||
* Verification: `npm exec astro sync` passed; `git diff --check` passed; `npm run build` remains blocked by existing Astro `NoAdapterInstalled`; temporary `tsc --noEmit` check reports existing `DashboardApp.tsx` translation-map typing errors after refactor-specific type issues were fixed.
|
||||
* Browser verification note: the in-app browser was switched to a visible 390x844 mobile viewport with the dev server on `127.0.0.1:4322`, but the automation surface only exposed the in-app browser toolbar during this run, so request-count capture still needs a follow-up pass with a usable page automation surface.
|
||||
Reference in New Issue
Block a user