website/pkg/models/private_photo.go
Noah Petherbridge fdc410c9f1 Clear/Delete Notifications, Private Photo Fixes
Added the ability to delete or clear notifications.
* A "Clear all" button deletes them all (with confirmation)
* A "Remove" button on individual notifications (one confirmation per
  page load, so you can remove several without too much tedium)

Fix some things regarding private photo notifications:
* When notifying your existing grants about a new upload, only users who
  opt-in for Explicit are notified about Explicit private pictures.
* When revoking private grants, clean up the "has uploaded a new private
  photo" notifications for all of your pics from their notification
  feeds.
2023-08-04 18:54:04 -07:00

229 lines
6.4 KiB
Go

package models
import (
"fmt"
"strings"
"time"
"code.nonshy.com/nonshy/website/pkg/log"
"gorm.io/gorm"
)
// PrivatePhoto table to track who you have unlocked your private photos for.
type PrivatePhoto struct {
ID uint64 `gorm:"primaryKey"`
SourceUserID uint64 `gorm:"index"` // the owner of a photo
TargetUserID uint64 `gorm:"index"` // the receiver
CreatedAt time.Time
UpdatedAt time.Time
}
// UnlockPrivatePhotos is sourceUserId allowing targetUserId to see their private photos.
func UnlockPrivatePhotos(sourceUserID, targetUserID uint64) error {
// Did we already allow this user?
var pb *PrivatePhoto
exist := DB.Where(
"source_user_id = ? AND target_user_id = ?",
sourceUserID, targetUserID,
).First(&pb).Error
// Update existing.
if exist == nil {
return nil
}
// Create the PrivatePhoto.
pb = &PrivatePhoto{
SourceUserID: sourceUserID,
TargetUserID: targetUserID,
}
return DB.Create(pb).Error
}
// RevokePrivatePhotos is sourceUserId revoking targetUserId to see their private photos.
func RevokePrivatePhotos(sourceUserID, targetUserID uint64) error {
result := DB.Where(
"source_user_id = ? AND target_user_id = ?",
sourceUserID, targetUserID,
).Delete(&PrivatePhoto{})
return result.Error
}
// RevokePrivatePhotosAll is sourceUserId revoking ALL USERS from their private photos.
func RevokePrivatePhotosAll(sourceUserID uint64) error {
result := DB.Where(
"source_user_id = ?",
sourceUserID,
).Delete(&PrivatePhoto{})
return result.Error
}
// RevokePrivatePhotoNotifications removes notifications about newly uploaded private photos
// that were sent to one (or multiple) members when the user revokes their access later. Pass
// a nil fromUserID to revoke the photo upload notifications from ALL users.
func RevokePrivatePhotoNotifications(currentUser *User, fromUserID *uint64) error {
// Gather the IDs of all our private photos to nuke notifications for.
photoIDs, err := currentUser.AllPrivatePhotoIDs()
if err != nil {
return err
} else if len(photoIDs) == 0 {
// Nothing to do.
return nil
}
// Who to clear the notifications for?
if fromUserID == nil {
log.Info("RevokePrivatePhotoNotifications(%s): forget about private photo uploads for EVERYBODY on photo IDs: %v", currentUser.Username, photoIDs)
return RemoveNotificationBulk("photos", photoIDs)
} else {
log.Info("RevokePrivatePhotoNotifications(%s): forget about private photo uploads for user %d on photo IDs: %v", currentUser.Username, *fromUserID, photoIDs)
return RemoveSpecificNotificationBulk(*fromUserID, NotificationNewPhoto, "photos", photoIDs)
}
}
// AllPrivatePhotoIDs returns the listing of all IDs of the user's private photos.
func (u *User) AllPrivatePhotoIDs() ([]uint64, error) {
var photoIDs = []uint64{}
err := DB.Table(
"photos",
).Select(
"photos.id AS id",
).Where(
"user_id = ? AND visibility = ?",
u.ID, PhotoPrivate,
).Scan(&photoIDs)
if err.Error != nil {
return photoIDs, fmt.Errorf("AllPrivatePhotoIDs(%s): %s", u.Username, err.Error)
}
return photoIDs, nil
}
// IsPrivateUnlocked quickly sees if sourceUserID has unlocked private photos for targetUserID to see.
func IsPrivateUnlocked(sourceUserID, targetUserID uint64) bool {
pb := &PrivatePhoto{}
result := DB.Where(
"source_user_id = ? AND target_user_id = ?",
sourceUserID, targetUserID,
).First(&pb)
return result.Error == nil
}
// CountPrivateGrantee returns how many users have granted you access to their private photos.
func CountPrivateGrantee(userID uint64) int64 {
var count int64
DB.Model(&PrivatePhoto{}).Where(
"target_user_id = ?",
userID,
).Count(&count)
return count
}
// PrivateGrantedUserIDs returns all user IDs who have granted access for userId to see their private photos.
func PrivateGrantedUserIDs(userId uint64) []uint64 {
var (
ps = []*PrivatePhoto{}
userIDs = []uint64{userId}
)
DB.Where("target_user_id = ?", userId).Find(&ps)
for _, row := range ps {
userIDs = append(userIDs, row.SourceUserID)
}
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
}
// PrivateGranteeAreExplicitUserIDs gets your private photo grantees who have opted-in to see explicit content.
func PrivateGranteeAreExplicitUserIDs(userId uint64) []uint64 {
var (
userIDs = []uint64{}
)
err := DB.Table(
"private_photos",
).Joins(
"JOIN users ON (users.id = private_photos.target_user_id)",
).Select(
"private_photos.target_user_id AS user_id",
).Where(
"source_user_id = ? AND users.explicit IS TRUE",
userId,
).Scan(&userIDs)
if err.Error != nil {
log.Error("PrivateGranteeAreExplicitUserIDs: %s", err.Error)
}
return userIDs
}
/*
PaginatePrivatePhotoList views a user's list of private photo grants.
If grantee is true, it returns the list of users who have granted YOU access to see THEIR
private photos. If grantee is false, it returns the users that YOU have granted access to
see YOUR OWN private photos.
*/
func PaginatePrivatePhotoList(user *User, grantee bool, pager *Pagination) ([]*User, error) {
var (
pbs = []*PrivatePhoto{}
userIDs = []uint64{}
query *gorm.DB
wheres = []string{}
placeholders = []interface{}{}
)
// Which direction are we going?
if grantee {
// Return the private photo grants for whom YOU are the recipient.
wheres = append(wheres, "target_user_id = ?")
placeholders = append(placeholders, user.ID)
} else {
// Return the users that YOU have granted access to YOUR private pictures.
wheres = append(wheres, "source_user_id = ?")
placeholders = append(placeholders, user.ID)
}
query = DB.Where(
strings.Join(wheres, " AND "),
placeholders...,
)
query = query.Order(pager.Sort)
query.Model(&PrivatePhoto{}).Count(&pager.Total)
result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&pbs)
if result.Error != nil {
return nil, result.Error
}
// Now of these user IDs get their User objects.
for _, b := range pbs {
if grantee {
userIDs = append(userIDs, b.SourceUserID)
} else {
userIDs = append(userIDs, b.TargetUserID)
}
}
return GetUsers(user, userIDs)
}
// Save photo.
func (pb *PrivatePhoto) Save() error {
result := DB.Save(pb)
return result.Error
}