perf: optimize web data resources
This commit is contained in:
@@ -48,10 +48,61 @@ Login and public marketing/legal pages are not part of the authenticated app she
|
||||
- Shared request behavior lives in `web/src/lib/api-client.ts`.
|
||||
- Auth/session behavior lives in `web/src/lib/auth.ts`.
|
||||
- Business API functions live in `web/src/lib/api.ts`.
|
||||
- Shared authenticated read caching lives in `web/src/lib/data-client.ts` and `web/src/lib/resources.ts`.
|
||||
- Components must call typed API helper functions, not inline `fetch('/api/...')`.
|
||||
- Components that need profile, points, packages, history, notifications, or unread-count data should use the resource hooks/functions from `web/src/lib/resources.ts` instead of starting their own duplicate GET lifecycle.
|
||||
- Dashboard-visible user, points, notification, and history data must come from the backend. Do not hardcode those values.
|
||||
- Production API host is `https://api.meeyao.com`; local dev should use same-origin `/api` and the Vite proxy.
|
||||
|
||||
### Authenticated Data Resource Pattern
|
||||
|
||||
Use this pattern for backend reads that are reused across authenticated pages:
|
||||
|
||||
```typescript
|
||||
// lib/api.ts: transport-only business API
|
||||
export function getPointsBalance(): Promise<PointsBalance> {
|
||||
return authFetch<PointsBalance>(API_ROUTES.points.balance);
|
||||
}
|
||||
|
||||
// lib/resources.ts: cache policy + hook/function surface
|
||||
export function usePoints() {
|
||||
return useResource({
|
||||
key: pointsBalanceKey,
|
||||
ttlMs: 60_000,
|
||||
fetcher: getPointsBalance,
|
||||
staleWhileRevalidate: true,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Resource contracts:
|
||||
|
||||
- `lib/api.ts` remains transport-only: no per-endpoint ad hoc memory cache there.
|
||||
- `lib/resources.ts` owns resource keys, TTLs, in-flight dedupe, stale-while-revalidate behavior, prefetch, and mutation invalidation.
|
||||
- `clearAuth()` must clear the shared data cache so authenticated data cannot leak across users.
|
||||
- Resource hooks must support disabled/optional keys for pages where an id may be absent; do not create a fetcher that intentionally rejects during normal render.
|
||||
- Active hooks must refetch after invalidation when they still need the resource.
|
||||
|
||||
Invalidation matrix:
|
||||
|
||||
- Profile, avatar, or settings write -> set the profile resource with the returned backend profile.
|
||||
- Divination run or follow-up completion -> invalidate points and the relevant history list/thread resources.
|
||||
- Notification mark-read -> patch the notification list and decrement unread count when the item changes from unread to read.
|
||||
- Mark-all-notifications-read -> patch the notification list and set unread count to zero.
|
||||
- Logout, expired refresh, or invalid auth -> clear auth and clear all resource data.
|
||||
|
||||
Wrong vs correct:
|
||||
|
||||
```typescript
|
||||
// Wrong: every page starts an independent duplicate GET.
|
||||
useEffect(() => {
|
||||
getUserProfile().then(setProfile);
|
||||
}, []);
|
||||
|
||||
// Correct: subscribe to the shared profile resource.
|
||||
const profileState = useProfile();
|
||||
```
|
||||
|
||||
## Layout Rules
|
||||
|
||||
- Build mobile-first, then add `sm:`, `md:`, `lg:`, and `xl:` refinements.
|
||||
|
||||
Reference in New Issue
Block a user