diff --git a/pkg/controller/photo/edit_delete.go b/pkg/controller/photo/edit_delete.go index 490500d..8893f45 100644 --- a/pkg/controller/photo/edit_delete.go +++ b/pkg/controller/photo/edit_delete.go @@ -52,17 +52,20 @@ func Edit() http.HandlerFunc { caption = r.FormValue("caption") isExplicit = r.FormValue("explicit") == "true" isGallery = r.FormValue("gallery") == "true" - visibility = r.FormValue("visibility") + visibility = models.PhotoVisibility(r.FormValue("visibility")) // Profile pic fields setProfilePic = r.FormValue("intent") == "profile-pic" crop = pphoto.ParseCropCoords(r.FormValue("crop")) + + // Are we GOING private? + goingPrivate = visibility == models.PhotoPrivate && visibility != photo.Visibility ) photo.Caption = caption photo.Explicit = isExplicit photo.Gallery = isGallery - photo.Visibility = models.PhotoVisibility(visibility) + photo.Visibility = visibility // Are we cropping ourselves a new profile pic? log.Error("Profile pic? %+v and crop is: %+v", setProfilePic, crop) @@ -99,6 +102,12 @@ func Edit() http.HandlerFunc { // Flash success. session.Flash(w, r, "Photo settings updated!") + // If this picture has moved to Private, revoke any notification we gave about it before. + if goingPrivate { + log.Info("The picture is GOING PRIVATE, revoke any notifications about it") + models.RemoveNotification("photos", photo.ID) + } + // Whose photo gallery to redirect to? if admin editing a user's photo, // go back to the owner's gallery instead of our own. if photo.UserID != currentUser.ID { diff --git a/pkg/controller/photo/upload.go b/pkg/controller/photo/upload.go index e539678..91b05da 100644 --- a/pkg/controller/photo/upload.go +++ b/pkg/controller/photo/upload.go @@ -131,6 +131,9 @@ func Upload() http.HandlerFunc { user.Save() } + // Notify all of our friends that we posted a new picture. + go notifyFriendsNewPhoto(p, user) + session.Flash(w, r, "Your photo has been uploaded successfully.") templates.Redirect(w, "/photo/u/"+user.Username) return @@ -142,3 +145,33 @@ func Upload() http.HandlerFunc { } }) } + +// Create a notification for all our Friends about a new photo. +// Run in a background goroutine in case it takes a while. +func notifyFriendsNewPhoto(photo *models.Photo, currentUser *models.User) { + var friendIDs []uint64 + + // Who to notify? + if photo.Visibility == models.PhotoPrivate { + // Private grantees + friendIDs = models.PrivateGranteeUserIDs(currentUser.ID) + log.Info("Notify %d private grantees about the new photo by %s", len(friendIDs), currentUser.Username) + } else { + // Friends + friendIDs = models.FriendIDs(currentUser.ID) + log.Info("Notify %d friends about the new photo by %s", len(friendIDs), currentUser.Username) + } + + for _, fid := range friendIDs { + notif := &models.Notification{ + UserID: fid, + AboutUser: *currentUser, + Type: models.NotificationNewPhoto, + TableName: "photos", + TableID: photo.ID, + } + if err := models.CreateNotification(notif); err != nil { + log.Error("Couldn't notify user %d about %s's new photo: %s", fid, currentUser.Username, err) + } + } +} diff --git a/pkg/models/notification.go b/pkg/models/notification.go index 05186f7..3a8e9da 100644 --- a/pkg/models/notification.go +++ b/pkg/models/notification.go @@ -39,12 +39,41 @@ const ( NotificationCertRejected = "cert_rejected" NotificationCertApproved = "cert_approved" NotificationPrivatePhoto = "private_photo" + NotificationNewPhoto = "new_photo" NotificationCustom = "custom" // custom message pushed ) // CreateNotification func CreateNotification(n *Notification) error { - return DB.Create(n).Error + // Insert via raw SQL query, reasoning: + // the AboutUser relationship has gorm do way too much work: + // - Upsert the user profile photo + // - Upsert the user profile fields + // - Upsert the user row itself + // .. and if we notify all your friends, all these wasteful queries ran + // for every single notification created! + if n.AboutUserID == nil && n.AboutUser.ID > 0 { + n.AboutUserID = &n.AboutUser.ID + } + return DB.Exec( + ` + INSERT INTO notifications + (user_id, about_user_id, type, read, table_name, table_id, message, link, created_at, updated_at) + VALUES + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, + n.UserID, + n.AboutUserID, + n.Type, + false, + n.TableName, + n.TableID, + n.Message, + n.Link, + time.Now(), + time.Now(), + ).Error + // return DB.Create(n).Error } // GetNotification by ID. diff --git a/pkg/models/private_photo.go b/pkg/models/private_photo.go index 66ee1cb..675c6ac 100644 --- a/pkg/models/private_photo.go +++ b/pkg/models/private_photo.go @@ -89,6 +89,19 @@ func PrivateGrantedUserIDs(userId uint64) []uint64 { return userIDs } +// PrivateGranteeUserIDs are the users whom WE have granted access to our photos (userId is the photo owners). +func PrivateGranteeUserIDs(userId uint64) []uint64 { + var ( + ps = []*PrivatePhoto{} + userIDs = []uint64{} + ) + DB.Where("source_user_id = ?", userId).Find(&ps) + for _, row := range ps { + userIDs = append(userIDs, row.TargetUserID) + } + return userIDs +} + /* PaginatePrivatePhotoList views a user's list of private photo grants. diff --git a/web/templates/account/dashboard.html b/web/templates/account/dashboard.html index d343e42..29d5c71 100644 --- a/web/templates/account/dashboard.html +++ b/web/templates/account/dashboard.html @@ -322,6 +322,23 @@ has granted you access to see their private photos! + {{else if eq .Type "new_photo"}} + + {{if and $Body.Photo (eq $Body.Photo.Visibility "private")}} + + {{else}} + + {{end}} + + + {{.AboutUser.Username}} + has uploaded a new + {{if and $Body.Photo (eq $Body.Photo.Visibility "private")}} + private photo! + {{else}} + photo! + {{end}} + {{else if eq .Type "cert_approved"}}