# Beacon System Architecture ## Overview Sojorn has **two separate posting systems** that share the same database but create slightly different content: 1. **Regular Posts** (compose_screen.dart) - Standard social media posts 2. **Beacon Posts** (create-beacon_sheet.dart) - GPS-tagged safety alerts ## How It Works ### Database Structure Both systems create records in the `posts` table, but with different flags: | Field | Regular Post | Beacon Post | |-------|-------------|-------------| | `is_beacon` | `FALSE` | `TRUE` | | `beacon_type` | `NULL` | `'police'`, `'checkpoint'`, `'taskForce'`, `'hazard'`, `'safety'`, or `'community'` | | `location` | `NULL` | GPS coordinates (PostGIS POINT) | | `confidence_score` | `NULL` | 0.5 - 1.0 (starts at 50-80% based on user trust) | | `is_active_beacon` | `NULL` | `TRUE` (becomes `FALSE` when pruned) | | `allow_chain` | User choice | Always `FALSE` | ### User Opt-In System **Critical Feature**: Beacon posts are **OPT-IN ONLY** for feeds. #### `profiles.beacon_enabled` Column - **Default**: `FALSE` (opted out) - **When FALSE**: User NEVER sees beacon posts in their Following or Sojorn feeds - **When TRUE**: User sees beacon posts mixed in with regular posts - **Beacon Map**: ALWAYS visible regardless of this setting #### Why Opt-In? Some users don't want safety alerts mixed into their social feed. The opt-in system allows: - **Casual users**: Just social content - **Community safety advocates**: Social content + beacons - **Everyone**: Can still view the Beacon Network map anytime ### Feed Filtering Logic #### feed-personal (Following Feed) ```typescript // Check user's beacon preference const { data: profile } = await supabase .from("profiles") .select("beacon_enabled") .eq("id", user.id) .single(); const beaconEnabled = profile?.beacon_enabled || false; // Build query let postsQuery = supabase.from("posts").select(...); // Filter out beacons if user has NOT opted in if (!beaconEnabled) { postsQuery = postsQuery.eq("is_beacon", false); } ``` #### feed-sojorn (Algorithmic Feed) Same logic - beacons are filtered unless `beacon_enabled = TRUE`. ### Beacon Creation Flow 1. User opens Beacon Network tab 2. Taps map to drop beacon 3. Fills out CreateBeaconSheet: - Type (police, checkpoint, etc.) - Title - Description - Optional photo 4. Submits → Edge function `create-beacon` 5. **Creates a POST in the `posts` table** with: - `is_beacon = TRUE` - `beacon_type = ` - `location = GPS point` - `category_id = "Beacon Alerts"` category - `confidence_score` based on user's trust score - `allow_chain = FALSE` ### Regular Post Creation Flow 1. User taps "New Post" button 2. Fills out ComposeScreen: - Community selection - Body text - Optional photo - Toggle for chain responses 3. Submits → Edge function `publish-post` 4. **Creates a POST in the `posts` table** with: - `is_beacon = FALSE` - No GPS data - User-selected category - User's chain preference ## Key Differences | Feature | Regular Post | Beacon Post | |---------|-------------|-------------| | **Purpose** | Social sharing | Safety alerts | | **GPS Data** | No | Required | | **Visible On** | Feeds (if user follows author) | Beacon map + feeds (if user opted in) | | **Category** | User selects | Always "Beacon Alerts" | | **Chaining** | User choice | Disabled | | **Confidence Score** | No | Yes (trust-based) | | **Voting** | No | Yes (vouch/report) | | **Auto-Pruning** | No | Yes (low confidence + old = disabled) | ## User Experience Scenarios ### Scenario 1: User With Beacons Disabled (Default) ``` Following Feed: ✓ Regular posts from people they follow Sojorn Feed: ✓ Algorithmic regular posts Beacon Map: ✓ All active beacons in area ``` ### Scenario 2: User With Beacons Enabled ``` Following Feed: ✓ Regular posts + beacons from people they follow Sojorn Feed: ✓ Algorithmic regular posts + beacons Beacon Map: ✓ All active beacons in area ``` ### Scenario 3: User Creates Beacon 1. Beacon appears on map IMMEDIATELY for ALL users 2. Beacon appears in creator's feed (if they have beacons enabled) 3. Beacon appears in OTHER users' feeds (if they follow creator AND have beacons enabled) ## Migration Required To enable this system, run: ```sql -- Add beacon_enabled column to profiles ALTER TABLE profiles ADD COLUMN IF NOT EXISTS beacon_enabled BOOLEAN NOT NULL DEFAULT FALSE; -- Add index for fast filtering CREATE INDEX IF NOT EXISTS idx_profiles_beacon_enabled ON profiles(beacon_enabled) WHERE beacon_enabled = TRUE; ``` Or apply the migration file: ```bash # Via Supabase Dashboard SQL Editor # Paste contents of: supabase/migrations/add_beacon_opt_in.sql ``` ## Edge Functions Updated 1. ✅ **feed-personal** - Now filters beacons based on user preference 2. ✅ **feed-sojorn** - Now filters beacons based on user preference 3. ✅ **create-beacon** - Creates beacon posts correctly 4. ✅ **publish-post** - Creates regular posts correctly ## Frontend Components - ✅ **ComposeScreen** - Regular post composer - ✅ **CreateBeaconSheet** - Beacon post composer - 🔲 **Settings Screen** - TODO: Add toggle for `beacon_enabled` preference - ✅ **BeaconScreen** - Shows beacons on map (always visible) - ✅ **FeedPersonalScreen** - Filtered feed - ✅ **FeedSojornScreen** - Filtered feed ## Next Steps 1. Apply database migration (`add_beacon_opt_in.sql`) 2. Deploy updated edge functions 3. Add UI toggle in user settings for beacon opt-in 4. Test both posting flows 5. Verify feed filtering works correctly