mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 10:19:43 +00:00
5.5 KiB
5.5 KiB
Mobile data retrieval review: task list
This task list targets the Home, Swipe, and Matches flows in apps/mobile, focusing on correctness, performance, and realtime consistency.
Overview (current state)
- Data fetching is split between Zustand stores (
useMatchStore,usePacksStore) and screen-local Supabase RPC calls (notablySwipeScreen). - Realtime updates are centralized in
apps/mobile/app/(app)/_layout.tsx(matches/messages/profile/pack-settings subscriptions). - Main risks today: missing query filters & error/loading states, redundant fetches, state races on param changes, and unread-count drift between realtime and the matches list.
Scope
- Screens
- Home:
apps/mobile/app/(app)/index.tsx - Swipe:
apps/mobile/app/(app)/swipe.tsx - Matches:
apps/mobile/app/(app)/matches.tsx
- Home:
- Stores
- Matches:
apps/mobile/src/store/matchStore.ts - Packs:
apps/mobile/src/store/packsStore.ts - Messages:
apps/mobile/src/store/messageStore.ts
- Matches:
- Realtime orchestration
apps/mobile/app/(app)/_layout.tsx
Priority 0 — correctness (must fix)
-
Home: fix packs count source-of-truth
- Current:
enabledPacksCount = packs.length(counts available packs). - Decide + implement:
- If label means enabled: use
enabledPackIds.length. - If label means available: rename the variable + label.
- If label means enabled: use
- Acceptance: packs number matches user expectations and store state.
- Current:
-
Swipe: normalize
packIdand reset state on param changes- Normalize
packIdfromuseLocalSearchParams()(string | string[]) before sending to RPC. - Reset
currentIndex,isLoading, and any derived UI state whenpackIdchanges. - Acceptance: switching packs never shows stale index/loading/UI.
- Normalize
-
Swipe: add race protection for overlapping async fetches
- Use a request-id guard (or abort pattern) so older
fetchQuestions()results cannot overwrite newer results. - Acceptance: rapid navigation/focus changes never clobber newer state.
- Use a request-id guard (or abort pattern) so older
-
Swipe: prevent stale “blocked” state on answer-gap errors
- Ensure
checkAnswerGap()clears/updatesisBlocked+gapInfodeterministically even when RPC errors. - Acceptance: blocked UI never “sticks” due to transient failures.
- Ensure
Priority 1 — UX and data freshness
-
Home: ensure data freshness on revisit
- Add
useFocusEffector a simple staleness TTL in stores. - Acceptance: counts (matches/new/packs) refresh when returning to Home.
- Add
-
Home: align “Recent Matches” semantics
useMatchStore.fetchMatches()sorts unread first; Home slices top 3.- Decide:
- True recent by
created_at, or - “Top matches” by unread-first.
- True recent by
- Acceptance: section name and behavior match.
-
Matches screen: wire up pull-to-refresh correctly
- Add
isLoading/isRefreshing+errortouseMatchStore. - Use those flags for
RefreshControl.refreshing. - Acceptance: pull-to-refresh shows spinner and recovers from errors.
- Add
Priority 2 — performance and query efficiency
-
MatchStore: add explicit filtering and guard empty
in(...)lists- Filter matches by
couple_id(in addition to RLS) for clarity/perf. - If no matches, avoid calling
.in('question_id', []). - Acceptance: fewer noisy requests; no empty-list edge behavior.
- Filter matches by
-
MatchStore: reduce unread-count work
- Current approach fetches one row per unread message and counts on client.
- Options:
- Add RPC/view that returns per-match unread counts.
- Or store a denormalized
unread_countper match (if acceptable).
- Acceptance: fetching matches scales with large message histories.
-
Swipe: remove heavy dependency on
fetchPacks()- Swipe often only needs
enabledPackIds. - Add lightweight
ensureEnabledPacksLoaded()or callfetchEnabledPacks()directly. - Acceptance: less data fetched when entering Swipe.
- Swipe often only needs
-
Swipe: replace random comparator sort
- Replace
Array.sort()comparator usingMath.random()with a stable score/shuffle pass. - Acceptance: consistent ordering logic; avoids comparator contract violations.
- Replace
Priority 3 — realtime correctness and consistency
-
Realtime messages: tighten subscription filter
- Current
messagessubscription listens without afilter. - Improve by filtering to the user’s couple scope (schema permitting) or alternative channel strategy.
- Acceptance: avoids noise; reduces risk of unintended cross-couple events.
- Current
-
Sync per-match unread counts with realtime events
- Currently global unread count updates live; per-match counts update only on
fetchMatches(). - Options:
- Update
useMatchStoreunreadCount per match when realtime message arrives. - Or trigger a targeted refresh when new partner message arrives.
- Update
- Acceptance: Matches list ordering/badges update as messages arrive.
- Currently global unread count updates live; per-match counts update only on
Optional: structural improvements (bigger refactors)
- Consolidate fetching into a query layer (e.g., TanStack Query) with caching, invalidation, retries, and focus refetch.
- Create a single “matches list” RPC that returns:
- match + question
- who-answered-first info (for two-part display)
- per-match unread counts
- new match flag
- Add lightweight integration tests around:
packIdparsing- request race guards
- unread count updates
Suggested implementation order
- P0 correctness (Home packs count, Swipe param/reset/races, gap-state)
- Matches loading/refresh UX
- Query efficiency (couple filters, unread-count optimization)
- Realtime tightening + unread sync
- Optional refactors