diff --git a/go-backend/internal/handlers/post_handler.go b/go-backend/internal/handlers/post_handler.go index 45b8075..b5c1126 100644 --- a/go-backend/internal/handlers/post_handler.go +++ b/go-backend/internal/handlers/post_handler.go @@ -588,7 +588,15 @@ func (h *PostHandler) GetProfilePosts(c *gin.Context) { viewerID = val.(string) } - posts, err := h.postRepo.GetPostsByAuthor(c.Request.Context(), authorID, viewerID, limit, offset, onlyChains) + // Check viewer's NSFW preference + showNSFW := false + if viewerID != "" { + if settings, err := h.userRepo.GetUserSettings(c.Request.Context(), viewerID); err == nil && settings.NSFWEnabled != nil { + showNSFW = *settings.NSFWEnabled + } + } + + posts, err := h.postRepo.GetPostsByAuthor(c.Request.Context(), authorID, viewerID, limit, offset, onlyChains, showNSFW) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch profile posts", "details": err.Error()}) return @@ -835,7 +843,15 @@ func (h *PostHandler) GetSavedPosts(c *gin.Context) { limit := utils.GetQueryInt(c, "limit", 20) offset := utils.GetQueryInt(c, "offset", 0) - posts, err := h.postRepo.GetSavedPosts(c.Request.Context(), userID, limit, offset) + // Check viewer's NSFW preference + showNSFW := false + if viewerID, exists := c.Get("user_id"); exists { + if settings, err := h.userRepo.GetUserSettings(c.Request.Context(), viewerID.(string)); err == nil && settings.NSFWEnabled != nil { + showNSFW = *settings.NSFWEnabled + } + } + + posts, err := h.postRepo.GetSavedPosts(c.Request.Context(), userID, limit, offset, showNSFW) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch saved posts", "details": err.Error()}) return @@ -861,7 +877,15 @@ func (h *PostHandler) GetLikedPosts(c *gin.Context) { limit := utils.GetQueryInt(c, "limit", 20) offset := utils.GetQueryInt(c, "offset", 0) - posts, err := h.postRepo.GetLikedPosts(c.Request.Context(), userID, limit, offset) + // Check viewer's NSFW preference + showNSFW := false + if viewerID, exists := c.Get("user_id"); exists { + if settings, err := h.userRepo.GetUserSettings(c.Request.Context(), viewerID.(string)); err == nil && settings.NSFWEnabled != nil { + showNSFW = *settings.NSFWEnabled + } + } + + posts, err := h.postRepo.GetLikedPosts(c.Request.Context(), userID, limit, offset, showNSFW) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch liked posts", "details": err.Error()}) return diff --git a/go-backend/internal/handlers/user_handler.go b/go-backend/internal/handlers/user_handler.go index a0fc5e7..a794b8f 100644 --- a/go-backend/internal/handlers/user_handler.go +++ b/go-backend/internal/handlers/user_handler.go @@ -255,7 +255,13 @@ func (h *UserHandler) GetSavedPosts(c *gin.Context) { limit := utils.GetQueryInt(c, "limit", 20) offset := utils.GetQueryInt(c, "offset", 0) - posts, err := h.postRepo.GetSavedPosts(c.Request.Context(), targetID, limit, offset) + // Check viewer's NSFW preference + showNSFW := false + if settings, err := h.repo.GetUserSettings(c.Request.Context(), currentUserID); err == nil && settings.NSFWEnabled != nil { + showNSFW = *settings.NSFWEnabled + } + + posts, err := h.postRepo.GetSavedPosts(c.Request.Context(), targetID, limit, offset, showNSFW) if err != nil { log.Error().Err(err).Msg("Failed to fetch saved posts") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch saved posts"}) @@ -284,7 +290,13 @@ func (h *UserHandler) GetLikedPosts(c *gin.Context) { limit := 20 offset := 0 - posts, err := h.postRepo.GetLikedPosts(c.Request.Context(), userID, limit, offset) + // Check viewer's NSFW preference + showNSFW := false + if settings, err := h.repo.GetUserSettings(c.Request.Context(), userID); err == nil && settings.NSFWEnabled != nil { + showNSFW = *settings.NSFWEnabled + } + + posts, err := h.postRepo.GetLikedPosts(c.Request.Context(), userID, limit, offset, showNSFW) if err != nil { log.Error().Err(err).Msg("Failed to fetch liked posts") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch liked posts"}) diff --git a/go-backend/internal/repository/post_repository.go b/go-backend/internal/repository/post_repository.go index 73e2f04..4cf5fd0 100644 --- a/go-backend/internal/repository/post_repository.go +++ b/go-backend/internal/repository/post_repository.go @@ -232,7 +232,7 @@ func (r *PostRepository) GetCategories(ctx context.Context) ([]models.Category, return categories, nil } -func (r *PostRepository) GetPostsByAuthor(ctx context.Context, authorID string, viewerID string, limit int, offset int, onlyChains bool) ([]models.Post, error) { +func (r *PostRepository) GetPostsByAuthor(ctx context.Context, authorID string, viewerID string, limit int, offset int, onlyChains bool, showNSFW bool) ([]models.Post, error) { query := ` SELECT p.id, p.author_id, p.category_id, p.body, @@ -268,10 +268,14 @@ func (r *PostRepository) GetPostsByAuthor(ctx context.Context, authorID string, ) ) AND ($5 = FALSE OR p.chain_parent_id IS NOT NULL) + AND ( + COALESCE(p.is_nsfw, FALSE) = FALSE + OR $6 = TRUE + ) ORDER BY p.created_at DESC LIMIT $2 OFFSET $3 ` - rows, err := r.pool.Query(ctx, query, authorID, limit, offset, viewerID, onlyChains) + rows, err := r.pool.Query(ctx, query, authorID, limit, offset, viewerID, onlyChains, showNSFW) if err != nil { return nil, err } @@ -549,7 +553,7 @@ func (r *PostRepository) GetNearbyBeacons(ctx context.Context, lat float64, long return beacons, nil } -func (r *PostRepository) GetSavedPosts(ctx context.Context, userID string, limit int, offset int) ([]models.Post, error) { +func (r *PostRepository) GetSavedPosts(ctx context.Context, userID string, limit int, offset int, showNSFW bool) ([]models.Post, error) { query := ` SELECT p.id, p.author_id, p.category_id, p.body, @@ -569,10 +573,11 @@ func (r *PostRepository) GetSavedPosts(ctx context.Context, userID string, limit JOIN public.profiles pr ON p.author_id = pr.id LEFT JOIN public.post_metrics m ON p.id = m.post_id WHERE ps.user_id = $1::uuid AND p.deleted_at IS NULL + AND (COALESCE(p.is_nsfw, FALSE) = FALSE OR $4 = TRUE) ORDER BY ps.created_at DESC LIMIT $2 OFFSET $3 ` - rows, err := r.pool.Query(ctx, query, userID, limit, offset) + rows, err := r.pool.Query(ctx, query, userID, limit, offset, showNSFW) if err != nil { return nil, err } @@ -601,7 +606,7 @@ func (r *PostRepository) GetSavedPosts(ctx context.Context, userID string, limit return posts, nil } -func (r *PostRepository) GetLikedPosts(ctx context.Context, userID string, limit int, offset int) ([]models.Post, error) { +func (r *PostRepository) GetLikedPosts(ctx context.Context, userID string, limit int, offset int, showNSFW bool) ([]models.Post, error) { query := ` SELECT p.id, p.author_id, p.category_id, p.body, @@ -621,10 +626,11 @@ func (r *PostRepository) GetLikedPosts(ctx context.Context, userID string, limit JOIN public.profiles pr ON p.author_id = pr.id LEFT JOIN public.post_metrics m ON p.id = m.post_id WHERE pl.user_id = $1::uuid AND p.deleted_at IS NULL + AND (COALESCE(p.is_nsfw, FALSE) = FALSE OR $4 = TRUE) ORDER BY pl.created_at DESC LIMIT $2 OFFSET $3 ` - rows, err := r.pool.Query(ctx, query, userID, limit, offset) + rows, err := r.pool.Query(ctx, query, userID, limit, offset, showNSFW) if err != nil { return nil, err } diff --git a/sojorn_app/lib/widgets/sojorn_post_card.dart b/sojorn_app/lib/widgets/sojorn_post_card.dart index 6bcb9ab..b7df583 100644 --- a/sojorn_app/lib/widgets/sojorn_post_card.dart +++ b/sojorn_app/lib/widgets/sojorn_post_card.dart @@ -91,12 +91,18 @@ class _sojornPostCardState extends ConsumerState { } } - /// Whether to show NSFW blur overlay — respects user's blur preference + /// Whether NSFW post should be completely hidden (not shown at all) + bool get _shouldHideNsfw { + if (!post.isNsfw) return false; + final settings = ref.read(settingsProvider); + return !(settings.user?.nsfwEnabled ?? false); + } + + /// Whether to show NSFW blur overlay — only applies when user HAS opted in bool get _shouldBlurNsfw { if (!post.isNsfw || _nsfwRevealed) return false; + if (_shouldHideNsfw) return false; // Will be hidden entirely, no blur needed final settings = ref.read(settingsProvider); - // Always blur if user hasn't opted into NSFW content - if (!(settings.user?.nsfwEnabled ?? false)) return true; // If opted in, respect the blur toggle return settings.user?.nsfwBlurEnabled ?? true; } @@ -114,6 +120,9 @@ class _sojornPostCardState extends ConsumerState { @override Widget build(BuildContext context) { + // Completely hide NSFW posts when user hasn't enabled NSFW + if (_shouldHideNsfw) return const SizedBox.shrink(); + return Material( color: Colors.transparent, child: Container(