diff --git a/crates/adapters/postgres/src/feed.rs b/crates/adapters/postgres/src/feed.rs index 2710eb7..2470db0 100644 --- a/crates/adapters/postgres/src/feed.rs +++ b/crates/adapters/postgres/src/feed.rs @@ -142,7 +142,7 @@ impl FeedRepository for PgFeedRepository { let ids: Vec = following_ids.iter().map(|id| id.as_uuid()).collect(); let viewer = viewer_id.map(|v| v.as_uuid()); let total: i64 = sqlx::query_scalar( - "SELECT COUNT(*) FROM thoughts t WHERE t.user_id=ANY($1) AND t.visibility='public'", + "SELECT COUNT(*) FROM thoughts t WHERE t.user_id=ANY($1) AND t.visibility != 'direct'", ) .bind(&ids) .fetch_one(&self.pool) @@ -150,7 +150,7 @@ impl FeedRepository for PgFeedRepository { .map_err(|e| DomainError::Internal(e.to_string()))?; let sel = feed_select(viewer); - let sql = format!("{sel} WHERE t.user_id=ANY($1) AND t.visibility='public' ORDER BY t.created_at DESC LIMIT $2 OFFSET $3"); + let sql = format!("{sel} WHERE t.user_id=ANY($1) AND t.visibility != 'direct' ORDER BY t.created_at DESC LIMIT $2 OFFSET $3"); let rows = sqlx::query_as::<_, FeedRow>(&sql) .bind(&ids) .bind(page.limit()) @@ -281,20 +281,25 @@ impl FeedRepository for PgFeedRepository { let viewer = viewer_id.map(|v| v.as_uuid()); let uid = user_id.as_uuid(); + // Use nil UUID for unauthenticated viewers — won't match owner or follower checks. + let viewer_uuid = viewer.unwrap_or(uuid::Uuid::nil()); + let total: i64 = sqlx::query_scalar( - "SELECT COUNT(*) FROM thoughts t WHERE t.user_id = $1 AND t.visibility = 'public'", + "SELECT COUNT(*) FROM thoughts t WHERE t.user_id = $1 AND ($2::uuid = $1 OR (t.visibility != 'direct' AND (t.visibility IN ('public', 'unlisted') OR (t.visibility = 'followers' AND EXISTS(SELECT 1 FROM follows WHERE follower_id = $2 AND following_id = $1 AND state = 'accepted')))))", ) .bind(uid) + .bind(viewer_uuid) .fetch_one(&self.pool) .await .map_err(|e| DomainError::Internal(e.to_string()))?; let sel = feed_select(viewer); - let sql = format!("{sel} WHERE t.user_id = $1 AND t.visibility = 'public' ORDER BY t.created_at DESC LIMIT $2 OFFSET $3"); + let sql = format!("{sel} WHERE t.user_id = $1 AND ($4::uuid = $1 OR (t.visibility != 'direct' AND (t.visibility IN ('public', 'unlisted') OR (t.visibility = 'followers' AND EXISTS(SELECT 1 FROM follows WHERE follower_id = $4 AND following_id = $1 AND state = 'accepted'))))) ORDER BY t.created_at DESC LIMIT $2 OFFSET $3"); let rows = sqlx::query_as::<_, FeedRow>(&sql) .bind(uid) .bind(page.limit()) .bind(page.offset()) + .bind(viewer_uuid) .fetch_all(&self.pool) .await .map_err(|e| DomainError::Internal(e.to_string()))?;