From 242333d8b71b6c437de57756dbba2a1f1bff682c Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Mon, 26 Aug 2024 21:36:48 -0700 Subject: [PATCH] Spit and polish * Show follower counts on forums * Sort by popularity (follow count) --- pkg/config/config.go | 2 +- pkg/controller/forum/browse.go | 2 + pkg/controller/forum/forum.go | 13 ++++--- pkg/controller/forum/forums.go | 9 +++-- pkg/models/forum.go | 6 +++ pkg/models/forum_membership.go | 56 ++++++++++++++++++++++++++++ web/templates/forum/board_index.html | 29 ++++++++++++++ web/templates/forum/index.html | 38 +++++++++++-------- 8 files changed, 128 insertions(+), 27 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 54c9be3..3a93a2f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -124,7 +124,7 @@ const ( ThreadViewDebounceCooldown = 1 * time.Hour // Enable user-owned forums (feature flag) - UserForumsEnabled = false + UserForumsEnabled = true ) // User-Owned Forums: Quota settings for how many forums a user can own. diff --git a/pkg/controller/forum/browse.go b/pkg/controller/forum/browse.go index 24379e9..194a5b3 100644 --- a/pkg/controller/forum/browse.go +++ b/pkg/controller/forum/browse.go @@ -23,6 +23,7 @@ func Explore() http.HandlerFunc { // Special sort handlers. // See PaginateForums for expanded handlers for these. + "by_followers", "by_latest", "by_threads", "by_posts", @@ -99,6 +100,7 @@ func Explore() http.HandlerFunc { "Categories": categorized, "ForumMap": forumMap, "FollowMap": followMap, + "FollowersMap": models.MapForumFollowers(forums), // Search filters "SearchTerm": searchTerm, diff --git a/pkg/controller/forum/forum.go b/pkg/controller/forum/forum.go index 5661935..41b88dd 100644 --- a/pkg/controller/forum/forum.go +++ b/pkg/controller/forum/forum.go @@ -81,12 +81,13 @@ func Forum() http.HandlerFunc { } var vars = map[string]interface{}{ - "Forum": forum, - "ForumModerators": mods, - "IsForumSubscribed": models.IsForumSubscribed(currentUser, forum), - "Threads": threads, - "ThreadMap": threadMap, - "Pager": pager, + "Forum": forum, + "ForumModerators": mods, + "ForumSubscriberCount": models.CountForumMemberships(forum), + "IsForumSubscribed": models.IsForumSubscribed(currentUser, forum), + "Threads": threads, + "ThreadMap": threadMap, + "Pager": pager, } if err := tmpl.Execute(w, r, vars); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/pkg/controller/forum/forums.go b/pkg/controller/forum/forums.go index 59fcdc8..f542a6a 100644 --- a/pkg/controller/forum/forums.go +++ b/pkg/controller/forum/forums.go @@ -76,10 +76,11 @@ func Landing() http.HandlerFunc { followMap := models.MapForumMemberships(currentUser, forums) var vars = map[string]interface{}{ - "Pager": pager, - "Categories": categorized, - "ForumMap": forumMap, - "FollowMap": followMap, + "Pager": pager, + "Categories": categorized, + "ForumMap": forumMap, + "FollowMap": followMap, + "FollowersMap": models.MapForumFollowers(forums), // Current viewer's forum quota. "ForumQuota": models.ComputeForumQuota(currentUser), diff --git a/pkg/models/forum.go b/pkg/models/forum.go index cb971b0..6c8a836 100644 --- a/pkg/models/forum.go +++ b/pkg/models/forum.go @@ -168,6 +168,12 @@ func PaginateForums(user *User, categories []string, search *Search, subscribed // Custom SORT parameters. switch pager.Sort { + case "by_followers": + pager.Sort = `( + SELECT count(forum_memberships.id) + FROM forum_memberships + WHERE forum_memberships.forum_id = forums.id + ) DESC` case "by_latest": pager.Sort = `( SELECT MAX(threads.updated_at) diff --git a/pkg/models/forum_membership.go b/pkg/models/forum_membership.go index 2201be1..1ee1894 100644 --- a/pkg/models/forum_membership.go +++ b/pkg/models/forum_membership.go @@ -166,6 +166,16 @@ func (u *User) HasForumSubscriptions() bool { return count > 0 } +// CountForumMemberships counts how many subscribers a forum has. +func CountForumMemberships(forum *Forum) int64 { + var count int64 + DB.Model(&ForumMembership{}).Where( + "forum_id = ?", + forum.ID, + ).Count(&count) + return count +} + // Save a forum membership. func (f *ForumMembership) Save() error { return DB.Save(f).Error @@ -233,3 +243,49 @@ func MapForumMemberships(user *User, forums []*Forum) ForumMembershipMap { return result } + +// ForumFollowerMap maps table IDs to counts of memberships. +type ForumFollowerMap map[uint64]int64 + +// Get like stats from the map. +func (fm ForumFollowerMap) Get(id uint64) int64 { + return fm[id] +} + +// MapForumFollowers maps out the count of followers for a set of forums. +func MapForumFollowers(forums []*Forum) ForumFollowerMap { + var ( + result = ForumFollowerMap{} + forumIDs = []uint64{} + ) + + // Initialize the result set. + for _, forum := range forums { + forumIDs = append(forumIDs, forum.ID) + } + + // Hold the result of the grouped count query. + type group struct { + ID uint64 + Followers int64 + } + var groups = []group{} + + // Map the counts of likes to each of these IDs. + if res := DB.Model( + &ForumMembership{}, + ).Select( + "forum_id AS id, count(id) AS followers", + ).Where( + "forum_id IN ?", + forumIDs, + ).Group("forum_id").Scan(&groups); res.Error != nil { + log.Error("MapLikes: count query: %s", res.Error) + } + + for _, row := range groups { + result[row.ID] = row.Followers + } + + return result +} diff --git a/web/templates/forum/board_index.html b/web/templates/forum/board_index.html index 6d6e458..4536719 100644 --- a/web/templates/forum/board_index.html +++ b/web/templates/forum/board_index.html @@ -200,6 +200,35 @@
Created on: {{.Forum.CreatedAt.Format "Jan _2 2006"}} + + {{if .ForumSubscriberCount}} +
+ + {{.ForumSubscriberCount}} {{if eq .ForumSubscriberCount 1}}person follows{{else}}people people{{end}} this forum. + + +
+ {{InputCSRF}} + + + {{if .IsForumSubscribed}} + + {{else}} + + {{end}} +
+
+ {{end}} +
{{if .Forum.Explicit}} diff --git a/web/templates/forum/index.html b/web/templates/forum/index.html index 0bae223..5095143 100644 --- a/web/templates/forum/index.html +++ b/web/templates/forum/index.html @@ -91,12 +91,13 @@ + - - - + + +
@@ -204,23 +205,28 @@
- {{if .Category}}
- by {{PrettyTitle}} -
- {{else}} -
- by - {{template "avatar-16x16" .Owner}} - {{if .Owner.Username}} - - {{or .Owner.Username "[unavailable]"}} - + {{if .Category}} + by {{PrettyTitle}} {{else}} - [unavailable] + by + {{template "avatar-16x16" .Owner}} + {{if .Owner.Username}} + + {{or .Owner.Username "[unavailable]"}} + + {{else}} + [unavailable] + {{end}} + {{end}} + + {{$FollowerCount := $Root.FollowersMap.Get .ID}} + {{if $FollowerCount}} + + {{$FollowerCount}} + {{end}}
- {{end}}