From 6cad3cadc7954f3ff560645066ccf39e2b6ea6e8 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Wed, 24 May 2023 11:27:42 -0700 Subject: [PATCH] Inner circle followups: notifications, min public pictures --- pkg/config/config.go | 3 +++ pkg/controller/photo/upload.go | 10 ++++++++-- pkg/controller/photo/user_gallery.go | 28 +++++++++++++++------------- pkg/models/friend.go | 25 +++++++++++++++++++++++++ pkg/models/photo.go | 16 ++++++++++++++++ web/templates/photo/gallery.html | 2 +- 6 files changed, 68 insertions(+), 16 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index cf32fbb..b374b63 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -87,6 +87,9 @@ const ( // Quotas for uploaded photos. PhotoQuotaUncertified = 6 PhotoQuotaCertified = 100 + + // Min number of public photos for inner circle members to see the prompt to invite. + InnerCircleMinimumPublicPhotos = 5 ) // Forum settings diff --git a/pkg/controller/photo/upload.go b/pkg/controller/photo/upload.go index d99683d..68c190a 100644 --- a/pkg/controller/photo/upload.go +++ b/pkg/controller/photo/upload.go @@ -157,8 +157,14 @@ func notifyFriendsNewPhoto(photo *models.Photo, currentUser *models.User) { friendIDs = models.PrivateGranteeUserIDs(currentUser.ID) log.Info("Notify %d private grantees about the new photo by %s", len(friendIDs), currentUser.Username) } else if photo.Visibility == models.PhotoInnerCircle { - friendIDs = models.FriendIDsInCircle(currentUser.ID) - log.Info("Notify %d circle friends about the new photo by %s", len(friendIDs), currentUser.Username) + // Inner circle members. If the pic is also Explicit, further narrow to explicit friend IDs. + if photo.Explicit { + friendIDs = models.FriendIDsInCircleAreExplicit(currentUser.ID) + log.Info("Notify %d EXPLICIT circle friends about the new photo by %s", len(friendIDs), currentUser.Username) + } else { + friendIDs = models.FriendIDsInCircle(currentUser.ID) + log.Info("Notify %d circle friends about the new photo by %s", len(friendIDs), currentUser.Username) + } } else { // Get all our friend IDs. If this photo is Explicit, only select // the friends who've opted-in for Explicit photo visibility. diff --git a/pkg/controller/photo/user_gallery.go b/pkg/controller/photo/user_gallery.go index 3f3325c..50f3087 100644 --- a/pkg/controller/photo/user_gallery.go +++ b/pkg/controller/photo/user_gallery.go @@ -139,19 +139,21 @@ func UserPhotos() http.HandlerFunc { commentMap := models.MapCommentCounts("photos", photoIDs) var vars = map[string]interface{}{ - "IsOwnPhotos": currentUser.ID == user.ID, - "IsShyUser": isShy, - "IsShyFrom": isShyFrom, - "IsMyPrivateUnlockedFor": isGranted, // have WE granted THIS USER to see our private pics? - "AreWeGrantedPrivate": isGrantee, // have THEY granted US private photo access. - "User": user, - "Photos": photos, - "PhotoCount": models.CountPhotos(user.ID), - "Pager": pager, - "LikeMap": likeMap, - "CommentMap": commentMap, - "ViewStyle": viewStyle, - "ExplicitCount": explicitCount, + "IsOwnPhotos": currentUser.ID == user.ID, + "IsShyUser": isShy, + "IsShyFrom": isShyFrom, + "IsMyPrivateUnlockedFor": isGranted, // have WE granted THIS USER to see our private pics? + "AreWeGrantedPrivate": isGrantee, // have THEY granted US private photo access. + "User": user, + "Photos": photos, + "PhotoCount": models.CountPhotos(user.ID), + "PublicPhotoCount": models.CountPublicPhotos(user.ID), + "InnerCircleMinimumPublicPhotos": config.InnerCircleMinimumPublicPhotos, + "Pager": pager, + "LikeMap": likeMap, + "CommentMap": commentMap, + "ViewStyle": viewStyle, + "ExplicitCount": explicitCount, } if err := tmpl.Execute(w, r, vars); err != nil { diff --git a/pkg/models/friend.go b/pkg/models/friend.go index 67289dc..2745a1a 100644 --- a/pkg/models/friend.go +++ b/pkg/models/friend.go @@ -155,6 +155,31 @@ func FriendIDsInCircle(userId uint64) []uint64 { return userIDs } +// FriendIDsInCircleAreExplicit returns the combined friend IDs who are in the inner circle + have opted in to explicit content. +// It is the combination of FriendIDsAreExplicit and FriendIDsInCircle. +func FriendIDsInCircleAreExplicit(userId uint64) []uint64 { + var ( + userIDs = []uint64{} + ) + + err := DB.Table( + "friends", + ).Joins( + "JOIN users ON (users.id = friends.target_user_id)", + ).Select( + "friends.target_user_id AS friend_id", + ).Where( + "friends.source_user_id = ? AND friends.approved = ? AND users.explicit = ? AND (users.inner_circle = ? OR users.is_admin = ?)", + userId, true, true, true, true, + ).Scan(&userIDs) + + if err.Error != nil { + log.Error("SQL error collecting explicit FriendIDs for %d: %s", userId, err) + } + + return userIDs +} + // CountFriendRequests gets a count of pending requests for the user. func CountFriendRequests(userID uint64) (int64, error) { var count int64 diff --git a/pkg/models/photo.go b/pkg/models/photo.go index 31ea4e3..30c1eb2 100644 --- a/pkg/models/photo.go +++ b/pkg/models/photo.go @@ -159,6 +159,22 @@ func CountExplicitPhotos(userID uint64, visibility []PhotoVisibility) (int64, er return count, result.Error } +// CountPublicPhotos returns the number of public photos on a user's page (to control whether the "invite to circle" prompt can appear). +func CountPublicPhotos(userID uint64) int64 { + query := DB.Where( + "user_id = ? AND visibility = ?", + userID, + PhotoPublic, + ) + + var count int64 + result := query.Model(&Photo{}).Count(&count) + if result.Error != nil { + log.Error("CountPublicPhotos(%d): %s", userID, result.Error) + } + return count +} + // DistinctPhotoTypes returns types of photos the user has: a set of public, friends, or private. // // The result is cached on the User the first time it's queried. diff --git a/web/templates/photo/gallery.html b/web/templates/photo/gallery.html index b4fda1e..3849ce1 100644 --- a/web/templates/photo/gallery.html +++ b/web/templates/photo/gallery.html @@ -342,7 +342,7 @@ {{if not .IsSiteGallery}} - {{if and (.CurrentUser.IsInnerCircle) (not .User.InnerCircle) (ne .CurrentUser.Username .User.Username)}} + {{if and (.CurrentUser.IsInnerCircle) (not .User.InnerCircle) (ne .CurrentUser.Username .User.Username) (ge .PublicPhotoCount .InnerCircleMinimumPublicPhotos)}}
Does {{.User.Username}} show a lot of nudity? Consider