Add comprehensive social graph implementation documentation
This commit is contained in:
parent
61165000a9
commit
9fec6754d9
169
sojorn_docs/SOCIAL_GRAPH_IMPLEMENTATION.md
Normal file
169
sojorn_docs/SOCIAL_GRAPH_IMPLEMENTATION.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# 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
|
||||
Loading…
Reference in a new issue