6.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Sauci is a couples intimacy app featuring swipeable question packs with partner matching. Users swipe on questions (yes/no/maybe), and when both partners answer positively, a "match" is created that unlocks a chat thread for that question.
Commands
# Install dependencies (from root)
npm install
# Start all apps in development
npm run dev
# Run specific apps
cd apps/mobile && npm run dev # Expo mobile app
cd apps/admin && npm run dev # React Admin dashboard
# Type checking
npm run typecheck
# Linting
npm run lint
# Build all
npm run build
Mobile-specific commands
cd apps/mobile
npm run ios # Run on iOS simulator
npm run android # Run on Android emulator
npm run web # Run in browser
Supabase commands
supabase start # Start local Supabase
supabase db reset # Reset DB and run all migrations
supabase functions serve # Run edge functions locally
supabase migration new <name> # Create new migration
Supabase Environments
There are two Supabase projects configured via MCP servers:
| Environment | Project Ref | URL | MCP Server |
|---|---|---|---|
| Production | ckjcrkjpmhqhiucifukx |
https://ckjcrkjpmhqhiucifukx.supabase.co | sauci-prod |
| Non-Production | itbzhrvlgvdmzbnhzhyx |
https://itbzhrvlgvdmzbnhzhyx.supabase.co | sauci-non-prod |
When using Claude Code with MCP tools:
- Use
mcp__sauci-prod__*tools for production database operations - Use
mcp__sauci-non-prod__*tools for development/staging database operations - Always verify which environment you're targeting before running migrations or destructive operations
Architecture
Monorepo Structure (Turborepo)
apps/mobile- Expo React Native app (expo-router for navigation)apps/admin- React Admin dashboard for content managementapps/supabase- Database migrations and edge functionspackages/shared- Shared TypeScript types (@sauci/shared)
Mobile App Architecture
- Routing: Expo Router with file-based routing (
app/directory)(auth)/- Authentication screens(app)/- Main app screens (protected)
- State Management: Zustand stores in
src/store/index.tsuseAuthStore- User, couple, partner stateuseMatchStore- Matches and notificationsusePacksStore- Question packs and enabled packs per coupleuseMessageStore- Chat messages and unread countsuseSubscriptionStore- RevenueCat subscription state
- Backend: Supabase client in
src/lib/supabase.ts - UI Components: Glass-morphism themed components in
src/components/ui/
Data Model (Core Entities)
- Profile - User profile extending Supabase auth (has
couple_id) - Couple - Two users linked via invite code
- QuestionPack - Collection of questions (can be premium)
- Question - Individual question with intensity level (1-5)
- Response - User's answer (yes/no/maybe) to a question
- Match - Created when both partners answer positively (yes_yes, yes_maybe, maybe_maybe)
Edge Functions
Edge functions in apps/supabase/functions/ handle complex operations:
submit-response- Saves response, checks for matches, triggers notificationsmanage-couple- Join/leave couple operationssend-notification- Push notifications for matchesrevenuecat-webhook- Subscription webhook handlersync-subscription- Syncs RevenueCat subscription status
Edge Function Deployment Guidelines
When deploying edge functions via MCP tools (mcp__sauci-*__deploy_edge_function):
IMPORTANT: Always use verify_jwt: false for all edge functions. Supabase's built-in JWT verification causes 401 errors even with valid tokens. Instead, handle authentication manually in the function code using:
const { data: { user }, error: authError } = await supabase.auth.getUser(
authHeader.replace("Bearer ", "")
);
This approach:
- Gives better error messages
- Allows custom auth handling
- Avoids mysterious 401 failures
Current function settings (all should be verify_jwt: false):
| Function | verify_jwt | Auth Method |
|---|---|---|
manage-couple |
false | Manual via getUser() |
submit-response |
false | Manual via getUser() |
sync-subscription |
false | Manual via getUser() |
delete-relationship |
false | Manual via getUser() |
revenuecat-webhook |
false | Webhook signature validation |
send-notification |
false | Internal trigger (no auth) |
send-message-notification |
false | Internal trigger (no auth) |
Database Best Practices
- Always use
.maybeSingle()instead of.single()when the row might not exist..single()throws an error if 0 or >1 rows are returned. - Profile creation trigger: The
on_auth_user_createdtrigger automatically creates a profile when a user signs up. Never assume a profile exists without checking. - Check for null profiles in edge functions before performing operations.
Key Patterns
- Responses use UPSERT with
onConflict: "user_id,question_id" - Match detection happens in
submit-responseedge function after each response - Real-time subscriptions used for chat messages
- RevenueCat handles in-app purchases and subscription management
- RLS policies enforce couple-level data isolation
Mobile UI - DO NOT CHANGE
Tab Bar Background
NEVER set backgroundColor: 'transparent' on the tab bar. Always use a semi-transparent solid color:
backgroundColor: 'rgba(18, 18, 18, 0.85)',
The BlurView in tabBarBackground is unreliable and can fail during re-renders/transitions, causing a fully transparent tab bar. The solid rgba background acts as a fallback while still allowing the blur effect on top.
Location: apps/mobile/app/(app)/_layout.tsx - tabBarStyle