Consumer app
The shopper experience lives in the (main) route group and navigates via a
floating blurred pill nav (BottomNav): Home, Explore, a center Preview action,
Messages, and Profile.
Getting set up
Before a try-on can work, the shopper completes a setup tunnel that also captures BIPA consent and writes the body photo.
| Screen | What it does |
|---|---|
onboarding.tsx | Taste onboarding (welcome → culture → style) with the first BIPA consent gate; writes gender/aesthetic/culture via updateProfile |
fit-setup.tsx | 5-step fit profile: gender → height (wheel) → weight (wheel, optional) → body photo → results. runAnalysis() posts to /api/analyze-body-photos, then writes user_measurements to AsyncStorage as bare keys. 120s timeout + error alert |
body-photo.tsx | Standalone body-photo capture/upload with direct camera/gallery pickers (guidance modals were removed to fix a picker loop) |
profile.tsx | View/edit the fit profile, body photo, measurement wheels, unit toggle, logout, and Delete Account |
Consent is enforced three times on the frontend and again at the backend (403 if not consented). See security.
Browsing
| Screen | What it does |
|---|---|
home.tsx | Personalized feed — wordmark header, hero, category pills, a two-column ProductCard grid, pull-to-refresh, and an error state with retry |
catalog.tsx | Browse / search / filter grid; merges backend products with local sample data; a search modal |
product/[id].tsx | The product page — color/size selectors, info tabs, similar products, and the Try It On button that opens the try-on screen |
The try-on render
try-on/[productId].tsx is the core screen (and its viewport is
frozen). It:
- Fetches the body photo from
GET /api/body-photoand only proceeds if the server confirms one exists — it never reuses a cached local photo, so a previous user's photo can't leak into a render. - Posts to
/api/try-on(the body photo is not in the request — it is fetched server-side), then either returns a cached result immediately or polls`/api/try-on/status/{jobId}`every 3 seconds up to a 180-second timeout. - Shows the
DrapeRenderLoaderduring the wait, then a BEFORE/AFTER view, a fit score, a color re-render option, and save / share / buy actions.
Errors are bucketed into friendly copy (rate-limit, timeout, abort, generic), and
the poll's AbortController is stored in a useRef and cancelled on unmount.
Saving & history
| Screen | What it does |
|---|---|
save-closet.tsx | Post-try-on save confirmation |
saved.tsx | Saved items / wishlist |
preview.tsx | Grid of the shopper's generated renders |
history.tsx | Try-on history, with a pending_history_writes retry queue for offline writes |
look-board.tsx | A grid of saved "looks" for a product or store |
Commerce & extras
| Screen | What it does |
|---|---|
orders.tsx | Consumer order history |
checkout-return.tsx (top-level) | Handles the Stripe checkout return, verifying the order by session_id |
scan.tsx | Camera QR/barcode scanner that resolves to a product or store |
sell.tsx | A peer-to-peer listing flow (category / condition) |
refer.tsx | Referral program — code, share sheet, stats |
messages.tsx / message-thread.tsx | Inbox and thread — currently static/mock data, no backend wiring |