# Social Graph & Privacy Implementation Status

## ✅ Completed Backend Features

### 1. Database Schema
- **Circle Members Table**: `public.circle_members` created with user_id/member_id pairs
- **SQL Function**: `is_in_circle(owner_id, user_id)` for efficient membership checks  
- **Migration**: `20260204000002_circle_privacy.up.sql` ready to apply

### 2. Repository Methods (Go Backend)

#### Followers & Following
- `GetFollowers(ctx, userID, limit, offset)` - Returns paginated list with harmony scores/tiers
- `GetFollowing(ctx, userID, limit, offset)` - Returns paginated list with harmony scores/tiers

#### Circle Management
- `AddToCircle(ctx, userID, memberID)` - Add user to circle (validates following first)
- `RemoveFromCircle(ctx, userID, memberID)` - Remove from circle
- `GetCircleMembers(ctx, userID)` - List all circle members
- `IsInCircle(ctx, ownerID, userID)` - Check membership

#### Data Export
- `ExportUserData(ctx, userID)` - Complete export (profile, posts, following list)

### 3. API Endpoints
All routes registered in `cmd/api/main.go`:

```
GET    /api/v1/users/:id/followers      - Get user's followers list
GET    /ap/v1/users/:id/following       - Get list of users they follow
POST   /api/v1/users/circle/:id         - Add user to your circle
DELETE /api/v1/users/circle/:id         - Remove user from circle
GET    /api/v1/users/circle/members     - Get your circle members
GET    /api/v1/users/me/export          - Export your complete data
```

## ⚠️ Remaining Implementation

### Circle Privacy in Feed Queries

Add this WHERE clause to `post_repository.go` in these methods:
- `GetFeed()` (line ~156)
- `GetPostsByAuthor()` (line ~243)  
- `GetPostByID()` (line ~310)

**Code to add** (after the blocking check):

```go
AND (
    p.visibility != 'circle'  -- Non-circle posts visible to all
    OR p.author_id = CASE WHEN $4::text != '' THEN $4::text::uuid ELSE NULL END  -- Author sees own circle posts
    OR public.is_in_circle(p.author_id, CASE WHENylt:text != '' THEN $4::text::uuid ELSE NULL END)  -- Circle members see circle posts
)
```

### Frontend Flutter Implementation

#### 1. Update Following Screen (`lib/screens/profile/following_screen.dart`)

Replace `_generateMockData()` with real API call:

```dart
Future<void> _loadFollowing() async {
  setState(() {
    _isLoading = true;
    _error = null;
  });

  try {
    final api = ref.read(apiServiceProvider);
    final currentUser = ref.read(currentUserProvider);
    
    final data = await api.callGoApi(
      '/users/${currentUser?.id}/following',
      queryParams: {'limit': '20', 'offset': '${_followedUsers.length}'},
    );
    
    final List<FollowedUser> users = (data['following'] as List)
        .map( (json) => FollowedUser.fromJson(json))
        .toList();
    
    setState(() {
      _followedUsers = users;
    });
  } catch (e) {
    setState(() {
      _error = e.toString();
    });
  } finally {
    setState(() {
      _isLoading = false;
    });
  }
}
```

#### 2. Create Followers Screen

Create `lib/screens/profile/followers_screen.dart` - similar structure to `FollowingScreen` but calling `/users/:id/followers`.

#### 3. Add Circle Management Screen

Create `lib/screens/settings/circle_management_screen.dart`:
- List current circle members (GET `/users/circle/members`)
- Show "Add to Circle" button on following list
- Handle add (POST `/users/circle/:id`) and remove (DELETE `/users/circle/:id`)

#### 4. Data Export Button

In `lib/screens/settings/profile_settings_screen.dart`:

```dart
ListTile(
  leading: Icon(Icons.download),
  title: Text('Export My Data'),
  subtitle: Text('Download your profile, posts, and connections'),
  onTap: () async {
    final api = ref.read(apiServiceProvider);
    final data = await api.callGoApi('/users/me/export');
    
    // Save to file
    final file = File('${documentsDir}/sojorn_export.json');
    await file.writeAsString(jsonEncode(data));
    
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Data exported successfully!')),
    );
  },
)
```

#### 5. Circle Visibility in Compose

In `lib/screens/compose/compose_screen.dart`, ensure "Circle" option in visibility dropdown:
- Options: 'public', 'friends', 'circle'
- When "Circle" is selected, post won't appear in feed for non-circle members

## Migration Instructions

1. **Apply Database Migration**:
   ```bash
   cd go-backend
   migrate -path internal/database/migrations -database "your_db_url" up
   ```

2. **Restart Go Backend** to load new routes

3. **Test Endpoints**:
   ```bash
   # Get followers
   curl -H "Authorization: Bearer $TOKEN" \
     "http://localhost:8080/api/v1/users/USER_ID/followers"
   
   # Export data
   curl -H "Authorization: Bearer $TOKEN" \
     "http://localhost:8080/api/v1/users/me/export" > export.json
   ```

4. **Update Flutter App**:
   - Implement the frontend screens listed above
   - Test circle visibility by creating circle-only posts
   - Verify data export downloads correctly

## Security Notes

- Circle membership is verified via `is_in_circle()` SQL function (performant, indexed)
- Blocked users cannot see any posts (via `has_block_between()`)
- Data export only returns user's own data (enforced by JWT user_id)
- All endpoints require authentication via JWT middleware
