Ability to ignore friend requests

This commit is contained in:
Noah Petherbridge 2023-10-22 19:57:18 -07:00
parent 61c47c032d
commit a97ed4562e
5 changed files with 104 additions and 22 deletions

View File

@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"code.nonshy.com/nonshy/website/pkg/config" "code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/log"
"code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/models"
"code.nonshy.com/nonshy/website/pkg/session" "code.nonshy.com/nonshy/website/pkg/session"
"code.nonshy.com/nonshy/website/pkg/templates" "code.nonshy.com/nonshy/website/pkg/templates"
@ -17,6 +18,7 @@ func Friends() http.HandlerFunc {
view = r.FormValue("view") view = r.FormValue("view")
isRequests = view == "requests" isRequests = view == "requests"
isPending = view == "pending" isPending = view == "pending"
isIgnored = view == "ignored"
) )
currentUser, err := session.CurrentUser(r) currentUser, err := session.CurrentUser(r)
@ -32,7 +34,7 @@ func Friends() http.HandlerFunc {
Sort: "updated_at desc", Sort: "updated_at desc",
} }
pager.ParsePage(r) pager.ParsePage(r)
friends, err := models.PaginateFriends(currentUser, isRequests, isPending, pager) friends, err := models.PaginateFriends(currentUser, isRequests, isPending, isIgnored, pager)
if err != nil { if err != nil {
session.FlashError(w, r, "Couldn't paginate friends: %s", err) session.FlashError(w, r, "Couldn't paginate friends: %s", err)
templates.Redirect(w, "/") templates.Redirect(w, "/")
@ -42,10 +44,18 @@ func Friends() http.HandlerFunc {
// Inject relationship booleans. // Inject relationship booleans.
models.SetUserRelationships(currentUser, friends) models.SetUserRelationships(currentUser, friends)
// Ignored friend request count.
ignoredFriendCount, err := models.CountIgnoredFriendRequests(currentUser.ID)
if err != nil {
log.Error("Ignored Friend Request Count (%s): %s", currentUser.Username, err)
}
var vars = map[string]interface{}{ var vars = map[string]interface{}{
"IsRequests": isRequests, "IsRequests": isRequests,
"IsPending": isPending, "IsPending": isPending,
"IsIgnored": isIgnored,
"Friends": friends, "Friends": friends,
"IgnoredFriendCount": ignoredFriendCount,
"Pager": pager, "Pager": pager,
} }
if err := tmpl.Execute(w, r, vars); err != nil { if err := tmpl.Execute(w, r, vars); err != nil {

View File

@ -71,6 +71,15 @@ func AddFriend() http.HandlerFunc {
templates.Redirect(w, "/friends?view=requests") templates.Redirect(w, "/friends?view=requests")
} }
templates.Redirect(w, "/friends") templates.Redirect(w, "/friends")
return
} else if verdict == "ignore" {
if err := models.IgnoreFriendRequest(currentUser, user); err != nil {
session.FlashError(w, r, "Error marking the friend request as ignored: %s", err)
} else {
session.Flash(w, r, "You have ignored the friend request from %s.", username)
}
templates.Redirect(w, "/friends")
return
} else { } else {
// Post the friend request. // Post the friend request.
if err := models.AddFriend(currentUser.ID, user.ID); err != nil { if err := models.AddFriend(currentUser.ID, user.ID); err != nil {

View File

@ -44,14 +44,6 @@ func SiteGallery() http.HandlerFunc {
sort = sortWhitelist[0] sort = sortWhitelist[0]
} }
// Defaults.
if viewStyle != "full" {
viewStyle = "cards"
}
if who != "friends" && who != "everybody" {
who = "friends"
}
// Load the current user. // Load the current user.
currentUser, err := session.CurrentUser(r) currentUser, err := session.CurrentUser(r)
if err != nil { if err != nil {
@ -59,7 +51,23 @@ func SiteGallery() http.HandlerFunc {
} }
// Is the current viewer shy? // Is the current viewer shy?
var isShy = currentUser.IsShy() var (
isShy = currentUser.IsShy()
myFriendCount = models.CountFriends(currentUser.ID)
)
// Defaults.
if viewStyle != "full" {
viewStyle = "cards"
}
if who != "friends" && who != "everybody" {
// Default Who setting should be Friends-only, unless you have no friends.
if myFriendCount > 0 {
who = "friends"
} else {
who = "everybody"
}
}
// Admin scope warning. // Admin scope warning.
if adminView && !currentUser.HasAdminScope(config.ScopePhotoModerator) { if adminView && !currentUser.HasAdminScope(config.ScopePhotoModerator) {

View File

@ -15,6 +15,7 @@ type Friend struct {
SourceUserID uint64 `gorm:"index"` SourceUserID uint64 `gorm:"index"`
TargetUserID uint64 `gorm:"index"` TargetUserID uint64 `gorm:"index"`
Approved bool `gorm:"index"` Approved bool `gorm:"index"`
Ignored bool
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
} }
@ -39,6 +40,7 @@ func AddFriend(sourceUserID, targetUserID uint64) error {
if reverse == nil && forward != nil { if reverse == nil && forward != nil {
// Approve the reverse. // Approve the reverse.
rev.Approved = true rev.Approved = true
rev.Ignored = false
rev.Save() rev.Save()
// Add the matching forward. // Add the matching forward.
@ -46,6 +48,7 @@ func AddFriend(sourceUserID, targetUserID uint64) error {
SourceUserID: sourceUserID, SourceUserID: sourceUserID,
TargetUserID: targetUserID, TargetUserID: targetUserID,
Approved: true, Approved: true,
Ignored: false,
} }
return DB.Create(f).Error return DB.Create(f).Error
} }
@ -240,13 +243,25 @@ func FriendIDsInCircleAreExplicit(userId uint64) []uint64 {
func CountFriendRequests(userID uint64) (int64, error) { func CountFriendRequests(userID uint64) (int64, error) {
var count int64 var count int64
result := DB.Where( result := DB.Where(
"target_user_id = ? AND approved = ?", "target_user_id = ? AND approved = ? AND ignored IS NOT true",
userID, userID,
false, false,
).Model(&Friend{}).Count(&count) ).Model(&Friend{}).Count(&count)
return count, result.Error return count, result.Error
} }
// CountIgnoredFriendRequests gets a count of ignored pending friend requests for the user.
func CountIgnoredFriendRequests(userID uint64) (int64, error) {
var count int64
result := DB.Where(
"target_user_id = ? AND approved = ? AND ignored = ?",
userID,
false,
true,
).Model(&Friend{}).Count(&count)
return count, result.Error
}
// CountFriends gets a count of friends for the user. // CountFriends gets a count of friends for the user.
func CountFriends(userID uint64) int64 { func CountFriends(userID uint64) int64 {
var count int64 var count int64
@ -269,7 +284,7 @@ The `requests` and `sent` bools are mutually exclusive (use only one, or neither
asks for unanswered friend requests to you, and `sent` returns the friend requests that you asks for unanswered friend requests to you, and `sent` returns the friend requests that you
have sent and have not been answered. have sent and have not been answered.
*/ */
func PaginateFriends(user *User, requests bool, sent bool, pager *Pagination) ([]*User, error) { func PaginateFriends(user *User, requests bool, sent bool, ignored bool, pager *Pagination) ([]*User, error) {
// We paginate over the Friend table. // We paginate over the Friend table.
var ( var (
fs = []*Friend{} fs = []*Friend{}
@ -277,20 +292,25 @@ func PaginateFriends(user *User, requests bool, sent bool, pager *Pagination) ([
query *gorm.DB query *gorm.DB
) )
if requests && sent { if requests && sent && ignored {
return nil, errors.New("requests and sent are mutually exclusive options, use one or neither") return nil, errors.New("requests and sent are mutually exclusive options, use one or neither")
} }
if requests { if requests {
query = DB.Where( query = DB.Where(
"target_user_id = ? AND approved = ?", "target_user_id = ? AND approved = ? AND ignored IS NOT true",
user.ID, false, user.ID, false,
) )
} else if sent { } else if sent {
query = DB.Where( query = DB.Where(
"source_user_id = ? AND approved = ?", "source_user_id = ? AND approved = ? AND ignored IS NOT true",
user.ID, false, user.ID, false,
) )
} else if ignored {
query = DB.Where(
"target_user_id = ? AND approved = ? AND ignored = ?",
user.ID, false, true,
)
} else { } else {
query = DB.Where( query = DB.Where(
"source_user_id = ? AND approved = ?", "source_user_id = ? AND approved = ?",
@ -307,7 +327,7 @@ func PaginateFriends(user *User, requests bool, sent bool, pager *Pagination) ([
// Now of these friends get their User objects. // Now of these friends get their User objects.
for _, friend := range fs { for _, friend := range fs {
if requests { if requests || ignored {
userIDs = append(userIDs, friend.SourceUserID) userIDs = append(userIDs, friend.SourceUserID)
} else { } else {
userIDs = append(userIDs, friend.TargetUserID) userIDs = append(userIDs, friend.TargetUserID)
@ -384,6 +404,28 @@ func GetFriendRequests(userID uint64) ([]*Friend, error) {
return fs, result.Error return fs, result.Error
} }
// IgnoreFriendRequest ignores a pending friend request that was sent to targetUserID.
func IgnoreFriendRequest(currentUser *User, fromUser *User) error {
// Is there a reverse friend request pending? (The one we ideally hope to mark Ignored)
rev := &Friend{}
reverse := DB.Where(
"source_user_id = ? AND target_user_id = ?",
fromUser.ID, currentUser.ID,
).First(&rev).Error
// If the reverse exists (requested us) mark it as Ignored.
if reverse == nil {
// Ignore the reverse friend request (happy path).
log.Error("%s ignoring friend request from %s", currentUser.Username, fromUser.Username)
rev.Approved = false
rev.Ignored = true
return rev.Save()
}
log.Error("rev: %+v", rev)
return errors.New("unexpected error while ignoring friend request")
}
// RemoveFriend severs a friend connection both directions, used when // RemoveFriend severs a friend connection both directions, used when
// rejecting a request or removing a friend. // rejecting a request or removing a friend.
func RemoveFriend(sourceUserID, targetUserID uint64) error { func RemoveFriend(sourceUserID, targetUserID uint64) error {

View File

@ -18,7 +18,7 @@
<div class="level-item"> <div class="level-item">
<div class="tabs is-toggle"> <div class="tabs is-toggle">
<ul> <ul>
<li{{if and (not .IsRequests) (not .IsPending)}} class="is-active"{{end}}> <li{{if and (not .IsRequests) (not .IsPending) (not .IsIgnored)}} class="is-active"{{end}}>
<a href="/friends">My Friends</a> <a href="/friends">My Friends</a>
</li> </li>
<li{{if .IsRequests}} class="is-active"{{end}}> <li{{if .IsRequests}} class="is-active"{{end}}>
@ -32,6 +32,12 @@
Sent Sent
</a> </a>
</li> </li>
<li{{if .IsIgnored}} class="is-active"{{end}}>
<a href="/friends?view=ignored">
Ignored
{{if .IgnoredFriendCount}}<span class="tag ml-2">{{.IgnoredFriendCount}}</span>{{end}}
</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -52,6 +58,7 @@
{{Pluralize64 .Pager.Total "has" "have"}} not been approved yet. {{Pluralize64 .Pager.Total "has" "have"}} not been approved yet.
{{else}} {{else}}
You have {{.Pager.Total}} friend{{if .IsRequests}} request{{end}}{{Pluralize64 .Pager.Total}} You have {{.Pager.Total}} friend{{if .IsRequests}} request{{end}}{{Pluralize64 .Pager.Total}}
{{if .IsIgnored}} on ignore -- they don't know you ignored their request and they can not send another one.{{end}}
(page {{.Pager.Page}} of {{.Pager.Pages}}). (page {{.Pager.Page}} of {{.Pager.Pages}}).
{{end}} {{end}}
</div> </div>
@ -108,7 +115,7 @@
</div> </div>
</div> </div>
</div> </div>
{{if $Root.IsRequests}} {{if or $Root.IsRequests $Root.IsIgnored}}
<footer class="card-footer"> <footer class="card-footer">
<button type="submit" name="verdict" value="approve" class="card-footer-item button is-success"> <button type="submit" name="verdict" value="approve" class="card-footer-item button is-success">
<span class="icon"><i class="fa fa-check"></i></span> <span class="icon"><i class="fa fa-check"></i></span>
@ -118,6 +125,12 @@
<span class="icon"><i class="fa fa-xmark"></i></span> <span class="icon"><i class="fa fa-xmark"></i></span>
<span>Reject</span> <span>Reject</span>
</button> </button>
{{if not $Root.IsIgnored}}
<button type="submit" name="verdict" value="ignore" class="card-footer-item button">
<span class="icon"><i class="fa fa-moon"></i></span>
<span>Ignore</span>
</button>
{{end}}
</footer> </footer>
{{else}} {{else}}
<footer class="card-footer"> <footer class="card-footer">