Skip to main content

What It Does

Collections are user-curated folders for organizing saved recipes and posts. They can be public (visible to everyone) or private (owner-only).

User Flow — Create

Navigate to app/collection/new.tsx → fill in name, description, public/private toggle (via ToggleSwitch) → supabase.from('collections').insert({...}).

User Flow — View

Tap collection → app/collection/[id].tsxCollectionDetailView renders collection info + grid of saved items.

User Flow — Edit

Owner taps edit → app/collection/[id]/edit.tsx → pre-populated form → can modify name, description, public/private toggle → supabase.from('collections').update({...}).

Screens & Components

Screen/ComponentPurpose
app/collection/new.tsxCollection creation
app/collection/[id].tsxCollection detail
app/collection/[id]/edit.tsxCollection edit flow
CollectionDetailViewDetail view with item grid
CollectionFormCreate/edit form

Hooks

useCollections (src/hooks/api/useCollections.ts)

Data Access Pattern

The mobile app calls Supabase directly. useCollection(id) uses supabase.rpc('get_collection_detail'), but useCollections does a direct table query: supabase.from('collections').select(...) with a JOIN to profiles — it does NOT use an RPC. Create, update, and delete also use direct table operations, not RPCs.

Mobile (Direct Supabase)

HookSupabase CallPurpose
useCollections(filters)supabase.from('collections').select(...)List collections (direct query, not RPC)
useCollection(id)supabase.rpc('get_collection_detail')Collection detail with paginated items
useCreateCollectionsupabase.from('collections').insert(...)Create collection
useUpdateCollectionsupabase.from('collections').update(...)Update (with ownership check)
useDeleteCollectionsupabase.from('collections').delete(...)Hard delete (CASCADE handles cleanup)
useSavedPageDatasupabase.rpc('get_saved_page_data')Saved tab overview data
useUserCollectionssupabase.from('collections').select(...)User’s collections for ContentSelector
useUncategorizedSavedItemssupabase.rpc('get_uncategorized_saved_items')Items not in any collection
useToggleSaveToCollectionsupabase.from('collection_saved_items').insert/deleteToggle collection membership
useDeleteCollection performs a HARD delete (supabase.from('collections').delete()), not a soft delete. The CASCADE constraint on collection_saved_items handles cleanup automatically.

Web API Routes (Phase 2)

MethodRoutePurpose
POST/api/collectionsCreate collection
GET/api/collections/[id]Get detail (calls get_collection_detail RPC)
PUT/api/collections/[id]Update collection
DELETE/api/collections/[id]Delete collection
GET/api/collections/listList collections (calls get_collections_list RPC)
POST/DELETE/api/collections/[id]/itemsAdd/remove items

RPC Functions

FunctionParametersReturns
get_collection_detailp_collection_id, p_current_user_id, p_item_limit (20), p_item_offset (0)JSON — collection with items, pagination
get_collections_listp_limit, p_offset, p_current_user_idJSON — paginated collection list
get_user_collections_for_save_modalp_user_id, p_saved_item_id (optional)TABLE(id, name, is_public, preview_image_url, item_count, is_in_collection) — used by SaveModal

Database Tables

TableKey ColumnsNotes
collectionsid, user_id (FK→profiles), name, description, cover_recipe_id (text, nullable — loose reference, no FK), is_public (default false), is_deleted (default false), item_count (denormalized, default 0)is_public controls visibility. is_deleted exists for RLS filtering but mobile useDeleteCollection does a hard delete.
collection_saved_itemscollection_id (FK→collections), saved_item_id (FK→saved_items), added_atComposite PK. Junction table.
collections.cover_recipe_id is typed as text (not uuid) and has no foreign key constraint — it’s a loose reference.

RLS Policies

collections:
  • SELECT: (is_deleted = false) OR (auth.uid() = user_id)
  • INSERT/UPDATE/DELETE: auth.uid() = user_id
collection_saved_items:
  • SELECT: true (public)
  • INSERT: Must own both the collection AND the saved_item (double ownership check)
  • DELETE: Must own the collection

Triggers

TriggerTableEventsFunction
trigger_update_profile_collection_countcollectionsINSERT, UPDATE, DELETEupdate_profile_collection_count()
trigger_update_collection_item_countcollection_saved_itemsINSERT, DELETEupdate_collection_item_count()
update_collections_updated_atcollectionsUPDATEupdate_updated_at_column()