Role-based Expo app for food discovery, order flow, delivery pickup verification, and basic moderation. This document is for developers working on code, schema, and environment setup.
expo@54,react-native@0.81,react@19, TypeScript- Navigation: React Navigation (native stack + bottom tabs)
- Backend: Supabase Auth + Postgres + Storage + RLS
- Device services: camera (
expo-camera), location (expo-location), document picker - Video: currently
expo-av(deprecated upstream; migration planned)
src/context/AppContext.tsx- central state and business logic
- auth/session lifecycle
- Supabase CRUD + RPC calls
- location, order status transitions, menu upload logic
src/navigation/RootNavigator.tsx- role-based tab trees
- screen-level UI and actions
src/types.ts- app domain types
utils/supabase.ts- Supabase client bootstrapping
utils/cloudinary.ts- optional Cloudinary upload helper
supabase/schema.sql- canonical idempotent schema + RLS policies + grants
supabase/*.sql- migration helpers and operational scripts
- Node 18+ and npm
- Expo Go or simulator/emulator
- Supabase project (for live mode)
npm installCopy .env.example to .env.local and set:
EXPO_PUBLIC_SUPABASE_URLEXPO_PUBLIC_SUPABASE_KEY
Optional:
EXPO_PUBLIC_CLOUDINARY_CLOUD_NAMEEXPO_PUBLIC_CLOUDINARY_UPLOAD_PRESETEXPO_PUBLIC_CLOUDINARY_FOLDER
Notes:
- Web upload path intentionally prefers Supabase storage to avoid browser tracking prevention issues with third-party media hosts.
EXPO_PUBLIC_OFFLINE_DEMO=1enables mock/demo mode.
npm run start
npm run start:tunnel
npm run web
npm run android
npm run ios
npm run demoRun SQL in this order:
supabase/schema_enum_app_role_legacy.sql(only if enum compatibility issue exists)supabase/schema.sql(always; safe to rerun)- Optional:
supabase/rpc_adjust_restaurant_like_count.sqlsupabase/storage_menu_videos_public_access.sqlsupabase/path-b-demo-roles.sql(after editing emails)
After schema updates, force PostgREST schema refresh when needed:
NOTIFY pgrst, 'reload schema';profiles- source of truth for role
- includes user metadata and location fields
restaurants- moderation state + social counters
- optional owner mapping for scoped restaurant role
menu_items- linked to restaurant
video_urlmay be supabase public path/url, signed-resolved url, or legacy cloudinary url
order_tickets- customer, restaurant, items payload, QR, status lifecycle
profile_change_requests- customer initiated role requests, admin resolution
Security is enforced at two layers:
- UI guardrails (role checks in app actions)
- RLS in Supabase (actual permission boundary)
Do not rely on UI checks alone. Any new privileged mutation should include a matching policy validation path.
- On auth state change:
- session sync
- profile fetch/ensure
- role sync from
profiles.role
- bootstrap on load
- periodic signed-in refresh (profile/catalog/requests)
- fallback to mock when Supabase unavailable/misconfigured
- customer places order ->
pending - admin/restaurant accepts ->
accepted+ QR - driver accepts ->
driver_accepted - driver scans customer QR ->
picked_up
- URL normalization for storage paths
- signed URL playback fallback in app context
- direct-open fallback in UI when embedded player fails
- web uploads prefer Supabase storage path
- GPS refresh with permission checks
- manual fallback setter
- profile location persistence when schema supports fields
- graceful fail-open when location columns are missing from live DB
- Add UI in
RootNavigator.tsx - Add action/state in
AppContext.tsx - Add/update domain types in
src/types.ts - Add/adjust RLS and schema if backend mutation changes
- Validate role checks in both UI and DB policy paths
- Update
supabase/schema.sqlidempotently (add column if not exists) - Update TS mapping functions in app context
- Add guards for backward compatibility where needed
- Run schema + cache refresh in Supabase
npx tsc --noEmitexpo-avdeprecation warning (migrate toexpo-video)RootNavigator.tsxandAppContext.tsxare large; candidates for modularization- web browser privacy settings can still affect third-party media URLs
- Re-run
supabase/schema.sql NOTIFY pgrst, 'reload schema';- reload app
- verify role in
profiles - confirm policy exists in schema version currently deployed
- ensure environment points to expected Supabase project
- inspect
[menu video]/[feed video]logs - verify storage object exists and is readable
- test with Chrome for web (tracking prevention can block third-party hosts)
- restart dev server
- prefer tunnel mode on unstable networks
Available scripts:
npm run eas:loginnpm run eas:configurenpm run eas:build:android:previewnpm run eas:build:ios:preview
Use EXPO_PUBLIC_* vars in EAS environment with non-secret visibility type.