Noah Petherbridge
e06bf2f9c4
* Friends: can now see your sent requests awaiting approval too. * Site Gallery: you may see Friends-only photos on the Gallery if you are friends with the owner and the pic is opted-in for the Gallery. * Site Gallery: show color coded visibility icons in card headers. * Improve appearance of Upload Photo page. * Update FAQ
202 lines
4.9 KiB
Go
202 lines
4.9 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Friend table.
|
|
type Friend struct {
|
|
ID uint64 `gorm:"primaryKey"`
|
|
SourceUserID uint64 `gorm:"index"`
|
|
TargetUserID uint64 `gorm:"index"`
|
|
Approved bool `gorm:"index"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// AddFriend sends a friend request or accepts one if there was already a pending one.
|
|
func AddFriend(sourceUserID, targetUserID uint64) error {
|
|
// Did we already send a friend request?
|
|
f := &Friend{}
|
|
forward := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).First(&f).Error
|
|
|
|
// Is there a reverse friend request pending?
|
|
rev := &Friend{}
|
|
reverse := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
targetUserID, sourceUserID,
|
|
).First(&rev).Error
|
|
|
|
// If the reverse exists (requested us) but not the forward, this completes the friendship.
|
|
if reverse == nil && forward != nil {
|
|
// Approve the reverse.
|
|
rev.Approved = true
|
|
rev.Save()
|
|
|
|
// Add the matching forward.
|
|
f = &Friend{
|
|
SourceUserID: sourceUserID,
|
|
TargetUserID: targetUserID,
|
|
Approved: true,
|
|
}
|
|
return DB.Create(f).Error
|
|
}
|
|
|
|
// If the forward already existed, error.
|
|
if forward == nil {
|
|
if f.Approved {
|
|
return errors.New("you are already friends")
|
|
}
|
|
return errors.New("a friend request had already been sent")
|
|
}
|
|
|
|
// Create the pending forward request.
|
|
f = &Friend{
|
|
SourceUserID: sourceUserID,
|
|
TargetUserID: targetUserID,
|
|
Approved: false,
|
|
}
|
|
return DB.Create(f).Error
|
|
}
|
|
|
|
// AreFriends quickly checks if two user IDs are friends.
|
|
func AreFriends(sourceUserID, targetUserID uint64) bool {
|
|
f := &Friend{}
|
|
DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).First(&f)
|
|
return f.Approved
|
|
}
|
|
|
|
// FriendStatus returns an indicator of friendship status: "none", "pending", "approved"
|
|
func FriendStatus(sourceUserID, targetUserID uint64) string {
|
|
f := &Friend{}
|
|
result := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).First(&f)
|
|
if result.Error == nil {
|
|
if f.Approved {
|
|
return "approved"
|
|
}
|
|
return "pending"
|
|
}
|
|
return "none"
|
|
}
|
|
|
|
// FriendIDs returns all user IDs with approved friendship to the user.
|
|
func FriendIDs(userId uint64) []uint64 {
|
|
var (
|
|
fs = []*Friend{}
|
|
userIDs = []uint64{}
|
|
)
|
|
DB.Where("source_user_id = ? AND approved = ?", userId, true).Find(&fs)
|
|
for _, row := range fs {
|
|
userIDs = append(userIDs, row.TargetUserID)
|
|
}
|
|
return userIDs
|
|
}
|
|
|
|
// CountFriendRequests gets a count of pending requests for the user.
|
|
func CountFriendRequests(userID uint64) (int64, error) {
|
|
var count int64
|
|
result := DB.Where(
|
|
"target_user_id = ? AND approved = ?",
|
|
userID,
|
|
false,
|
|
).Model(&Friend{}).Count(&count)
|
|
return count, result.Error
|
|
}
|
|
|
|
/*
|
|
PaginateFriends gets a page of friends (or pending friend requests) as User objects ordered
|
|
by friendship date.
|
|
|
|
The `requests` and `sent` bools are mutually exclusive (use only one, or neither). `requests`
|
|
asks for unanswered friend requests to you, and `sent` returns the friend requests that you
|
|
have sent and have not been answered.
|
|
*/
|
|
func PaginateFriends(userID uint64, requests bool, sent bool, pager *Pagination) ([]*User, error) {
|
|
// We paginate over the Friend table.
|
|
var (
|
|
fs = []*Friend{}
|
|
userIDs = []uint64{}
|
|
query *gorm.DB
|
|
)
|
|
|
|
if requests && sent {
|
|
return nil, errors.New("requests and sent are mutually exclusive options, use one or neither")
|
|
}
|
|
|
|
if requests {
|
|
query = DB.Where(
|
|
"target_user_id = ? AND approved = ?",
|
|
userID, false,
|
|
)
|
|
} else if sent {
|
|
query = DB.Where(
|
|
"source_user_id = ? AND approved = ?",
|
|
userID, false,
|
|
)
|
|
} else {
|
|
query = DB.Where(
|
|
"source_user_id = ? AND approved = ?",
|
|
userID, true,
|
|
)
|
|
}
|
|
|
|
query = query.Order(pager.Sort)
|
|
query.Model(&Friend{}).Count(&pager.Total)
|
|
result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&fs)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
|
|
// Now of these friends get their User objects.
|
|
for _, friend := range fs {
|
|
if requests {
|
|
userIDs = append(userIDs, friend.SourceUserID)
|
|
} else {
|
|
userIDs = append(userIDs, friend.TargetUserID)
|
|
}
|
|
}
|
|
|
|
return GetUsers(userIDs)
|
|
}
|
|
|
|
// GetFriendRequests returns all pending friend requests for a user.
|
|
func GetFriendRequests(userID uint64) ([]*Friend, error) {
|
|
var fs = []*Friend{}
|
|
result := DB.Where(
|
|
"target_user_id = ? AND approved = ?",
|
|
userID,
|
|
false,
|
|
).Find(&fs)
|
|
return fs, result.Error
|
|
}
|
|
|
|
// RemoveFriend severs a friend connection both directions, used when
|
|
// rejecting a request or removing a friend.
|
|
func RemoveFriend(sourceUserID, targetUserID uint64) error {
|
|
result := DB.Where(
|
|
"(source_user_id = ? AND target_user_id = ?) OR "+
|
|
"(target_user_id = ? AND source_user_id = ?)",
|
|
sourceUserID, targetUserID,
|
|
sourceUserID, targetUserID,
|
|
).Delete(&Friend{})
|
|
return result.Error
|
|
}
|
|
|
|
// Save photo.
|
|
func (f *Friend) Save() error {
|
|
result := DB.Save(f)
|
|
return result.Error
|
|
}
|