From 67a54c866e9c0bdef00746c11395a05ac31e4df4 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Fri, 1 Sep 2023 17:12:27 -0700 Subject: [PATCH] New search filters and friendship sent icon --- pkg/controller/account/search.go | 10 +++ pkg/models/friend.go | 42 +++++++++++++ pkg/models/shy_accounts.go | 98 ++++++++++++++++++++++++++++++ pkg/models/user.go | 49 ++++++++------- web/templates/account/profile.html | 2 +- web/templates/account/search.html | 57 ++++++++++++++--- web/templates/friend/friends.html | 10 +++ 7 files changed, 232 insertions(+), 36 deletions(-) create mode 100644 pkg/models/shy_accounts.go diff --git a/pkg/controller/account/search.go b/pkg/controller/account/search.go index 8aed64e..834d2a4 100644 --- a/pkg/controller/account/search.go +++ b/pkg/controller/account/search.go @@ -36,6 +36,7 @@ func Search() http.HandlerFunc { orientation = r.FormValue("orientation") maritalStatus = r.FormValue("marital_status") hereFor = r.FormValue("here_for") + friendSearch = r.FormValue("friends") == "true" sort = r.FormValue("sort") sortOK bool ageMin int @@ -93,6 +94,8 @@ func Search() http.HandlerFunc { HereFor: hereFor, Certified: isCertified != "false", InnerCircle: isCertified == "circle", + ShyAccounts: isCertified == "shy", + Friends: friendSearch, AgeMin: ageMin, AgeMax: ageMax, }, pager) @@ -117,11 +120,18 @@ func Search() http.HandlerFunc { "EmailOrUsername": username, "AgeMin": ageMin, "AgeMax": ageMax, + "FriendSearch": friendSearch, "Sort": sort, // Photo counts mapped to users "PhotoCountMap": models.MapPhotoCounts(users), + // Map Shy Account badges for these results + "ShyMap": models.MapShyAccounts(users), + + // Map friendships to these users. + "FriendMap": models.MapFriends(currentUser, users), + // Current user's location setting. "MyLocation": myLocation, "GeoIPInsights": insights, diff --git a/pkg/models/friend.go b/pkg/models/friend.go index 989bf4e..6f49ea8 100644 --- a/pkg/models/friend.go +++ b/pkg/models/friend.go @@ -297,3 +297,45 @@ func (f *Friend) Save() error { result := DB.Save(f) return result.Error } + +// FriendMap maps user IDs to friendship status for the current user. +type FriendMap map[uint64]bool + +// MapFriends looks up a set of user IDs in bulk and returns a FriendMap suitable for templates. +func MapFriends(currentUser *User, users []*User) FriendMap { + var ( + usermap = FriendMap{} + set = map[uint64]interface{}{} + distinct = []uint64{} + ) + + // Uniqueify users. + for _, user := range users { + if _, ok := set[user.ID]; ok { + continue + } + set[user.ID] = nil + distinct = append(distinct, user.ID) + } + + var ( + matched = []*Friend{} + result = DB.Model(&Friend{}).Where( + "source_user_id = ? AND target_user_id IN ? AND approved = ?", + currentUser.ID, distinct, true, + ).Find(&matched) + ) + + if result.Error == nil { + for _, row := range matched { + usermap[row.TargetUserID] = true + } + } + + return usermap +} + +// Get a user from the FriendMap. +func (um FriendMap) Get(id uint64) bool { + return um[id] +} diff --git a/pkg/models/shy_accounts.go b/pkg/models/shy_accounts.go new file mode 100644 index 0000000..8777ed1 --- /dev/null +++ b/pkg/models/shy_accounts.go @@ -0,0 +1,98 @@ +package models + +// Supplementary functions to do with Shy Accounts. + +import "code.nonshy.com/nonshy/website/pkg/log" + +// IsShy returns whether the user might have an "empty" profile from the perspective of anybody. +// +// An empty profile means their profile is Private or else ALL of their photos are non-public; so that +// somebody viewing their page might see nothing at all from them and consider them a "blank" profile. +func (u *User) IsShy() bool { + // NOTE: if you change the logic for Shy Accounts, also align your changes + // in the functions: WhereClauseShyAccounts. + + // Non-certified users are considered empty. + if !u.Certified { + return true + } + + // Private profile automatically applies. + if u.Visibility == UserVisibilityPrivate { + return true + } + + // If ALL of our photos are non-public, that counts too. + var photoTypes = u.DistinctPhotoTypes() + if _, ok := photoTypes[PhotoPublic]; !ok { + log.Info("IsEmptyProfile: true because visibilities %+v did not include public", photoTypes) + return true + } + + return false +} + +// WhereClauseShyAccounts returns SQL query fragments when running User table queries +// that will filter for shy accounts. +// +// This is used by SearchUsers and MapShyAccounts. +func WhereClauseShyAccounts() (where string, placeholders []interface{}) { + where = `( + certified IS NOT true + OR visibility = ? + OR NOT EXISTS ( + SELECT 1 FROM photos + WHERE user_id = users.id + AND visibility = ? + ) + )` + placeholders = []interface{}{ + UserVisibilityPrivate, + PhotoPublic, + } + return +} + +// ShyMap maps user IDs to Shy Account status in bulk queries. +type ShyMap map[uint64]bool + +// MapShyAccounts looks up a set of user IDs in bulk and returns a ShyMap suitable for templates. +func MapShyAccounts(users []*User) ShyMap { + var ( + usermap = ShyMap{} + set = map[uint64]interface{}{} + distinct = []uint64{} + ) + + // Uniqueify users. + for _, user := range users { + if _, ok := set[user.ID]; ok { + continue + } + set[user.ID] = nil + distinct = append(distinct, user.ID) + } + + var ( + matched = []*User{} + where, placeholders = WhereClauseShyAccounts() + result = (&User{}). + Preload(). + Where("id IN ?", distinct). + Where(where, placeholders...). + Find(&matched) + ) + + if result.Error == nil { + for _, row := range matched { + usermap[row.ID] = true + } + } + + return usermap +} + +// Get a user from the ShyMap. +func (um ShyMap) Get(id uint64) bool { + return um[id] +} diff --git a/pkg/models/user.go b/pkg/models/user.go index ec73b66..3e94087 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -139,31 +139,6 @@ func FindUser(username string) (*User, error) { return u, result.Error } -// IsShy returns whether the user might have an "empty" profile from the perspective of anybody. -// -// An empty profile means their profile is Private or else ALL of their photos are non-public; so that -// somebody viewing their page might see nothing at all from them and consider them a "blank" profile. -func (u *User) IsShy() bool { - // Non-certified users are considered empty. - if !u.Certified { - return true - } - - // Private profile automatically applies. - if u.Visibility == UserVisibilityPrivate { - return true - } - - // If ALL of our photos are non-public, that counts too. - var photoTypes = u.DistinctPhotoTypes() - if _, ok := photoTypes[PhotoPublic]; !ok { - log.Info("IsEmptyProfile: true because visibilities %+v did not include public", photoTypes) - return true - } - - return false -} - // IsShyFrom tells whether the user is shy from the perspective of the other user. // // That is, depending on our profile visibility and friendship status. @@ -191,6 +166,8 @@ type UserSearch struct { HereFor string Certified bool InnerCircle bool + ShyAccounts bool + Friends bool AgeMin int AgeMax int } @@ -289,6 +266,7 @@ func SearchUsers(user *User, search *UserSearch, pager *Pagination) ([]*User, er placeholders = append(placeholders, "here_for", "%"+search.HereFor+"%") } + // Certified filter (including if Shy Accounts are asked for) if search.Certified { wheres = append(wheres, "certified = ?", "status = ?") placeholders = append(placeholders, search.Certified, UserStatusActive) @@ -299,6 +277,27 @@ func SearchUsers(user *User, search *UserSearch, pager *Pagination) ([]*User, er placeholders = append(placeholders, true, true) } + if search.ShyAccounts { + a, b := WhereClauseShyAccounts() + wheres = append(wheres, a) + placeholders = append(placeholders, b...) + } + + if search.Friends { + wheres = append(wheres, ` + EXISTS ( + SELECT 1 FROM friends + WHERE source_user_id = ? + AND target_user_id = users.id + AND approved = ? + ) + `) + placeholders = append(placeholders, + user.ID, + true, + ) + } + if search.AgeMin > 0 { date := time.Now().AddDate(-search.AgeMin, 0, 0) wheres = append(wheres, "birthdate <= ?") diff --git a/web/templates/account/profile.html b/web/templates/account/profile.html index 2c2673c..be72a4f 100644 --- a/web/templates/account/profile.html +++ b/web/templates/account/profile.html @@ -164,7 +164,7 @@ {{if eq .IsFriend "approved"}} {{else if eq .IsFriend "pending"}} - + {{else}} {{end}} diff --git a/web/templates/account/search.html b/web/templates/account/search.html index 2e40380..1611093 100644 --- a/web/templates/account/search.html +++ b/web/templates/account/search.html @@ -29,7 +29,11 @@ - {{if not (eq .Sort "distance")}} + {{if .FriendSearch}} +
+ Currently searching within your Friends list. +
+ {{else if not (eq .Sort "distance")}}
New feature: you can now see Who's Nearby! {{if not .MyLocation.Source}} @@ -72,22 +76,24 @@
-
+
-
+
-
+
@@ -125,7 +131,7 @@
-
+
@@ -142,7 +148,7 @@
-
+
@@ -156,7 +162,7 @@
-
+
@@ -170,7 +176,7 @@
-
+
@@ -184,7 +190,21 @@
-
+
+
+ + +
+
+ +
@@ -201,7 +221,7 @@
-
+
Reset