Spit and polish
* Show follower counts on forums * Sort by popularity (follow count)
This commit is contained in:
parent
56a6190ce9
commit
242333d8b7
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -200,6 +200,35 @@
|
|||
<label class="label"><i class="fa fa-info-circle"></i> Forum Info</label>
|
||||
<div class="mb-4">
|
||||
Created on: <span title="{{.Forum.CreatedAt}}">{{.Forum.CreatedAt.Format "Jan _2 2006"}}</span>
|
||||
|
||||
{{if .ForumSubscriberCount}}
|
||||
<div class="has-text-info mt-2">
|
||||
<i class="fa fa-book-bookmark mr-1"></i>
|
||||
{{.ForumSubscriberCount}} {{if eq .ForumSubscriberCount 1}}person follows{{else}}people people{{end}} this forum.
|
||||
|
||||
<!-- Follow/Unfollow This Forum -->
|
||||
<form action="/forum/subscribe" method="POST" class="is-inline">
|
||||
{{InputCSRF}}
|
||||
<input type="hidden" name="fragment" value="{{.Forum.Fragment}}">
|
||||
|
||||
{{if .IsForumSubscribed}}
|
||||
<button type="submit" class="button is-small ml-2"
|
||||
name="intent" value="unfollow"
|
||||
onclick="return confirm('Do you want to remove this forum from your list?')">
|
||||
<span class="icon"><i class="fa fa-bookmark"></i></span>
|
||||
<span>Unfollow</span>
|
||||
</button>
|
||||
{{else}}
|
||||
<button type="submit" class="button is-small ml-2"
|
||||
name="intent" value="follow">
|
||||
<span class="icon"><i class="fa-regular fa-bookmark has-text-success"></i></span>
|
||||
<span>Follow it too?</span>
|
||||
</button>
|
||||
{{end}}
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="mt-2">
|
||||
{{if .Forum.Explicit}}
|
||||
<span class="tag is-danger is-light">
|
||||
|
|
|
@ -91,12 +91,13 @@
|
|||
<option value="title asc"{{if eq .Sort "title asc"}} selected{{end}}>Title (A-Z)</option>
|
||||
<option value="title desc"{{if eq .Sort "title desc"}} selected{{end}}>Title (Z-A)</option>
|
||||
<option value="created_at desc"{{if eq .Sort "created_at desc"}} selected{{end}}>Recently created</option>
|
||||
<option value="by_followers"{{if eq .Sort "by_followers"}} selected{{end}}>Popularity (follower count)</option>
|
||||
</optgroup>
|
||||
<optgroup label="Contents">
|
||||
<option value="by_latest"{{if eq .Sort "by_latest"}} selected{{end}}>Latest post</option>
|
||||
<option value="by_threads"{{if eq .Sort "by_threads"}} selected{{end}}>Topic count</option>
|
||||
<option value="by_posts"{{if eq .Sort "by_posts"}} selected{{end}}>Post count</option>
|
||||
<option value="by_users"{{if eq .Sort "by_users"}} selected{{end}}>User count</option>
|
||||
<option value="by_threads"{{if eq .Sort "by_threads"}} selected{{end}}>Topics (count of threads)</option>
|
||||
<option value="by_posts"{{if eq .Sort "by_posts"}} selected{{end}}>Posts (count of threads and replies)</option>
|
||||
<option value="by_users"{{if eq .Sort "by_users"}} selected{{end}}>Users (distinct members who have posted)</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -204,23 +205,28 @@
|
|||
</div>
|
||||
|
||||
<!-- Owner line -->
|
||||
{{if .Category}}
|
||||
<div class="mt-2 has-text-grey" style="font-size: smaller">
|
||||
by <a href="/f/{{.Fragment}}">{{PrettyTitle}}</a>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="mt-2 has-text-grey" style="font-size: smaller">
|
||||
by
|
||||
{{template "avatar-16x16" .Owner}}
|
||||
{{if .Owner.Username}}
|
||||
<a href="/u/{{.Owner.Username}}" class="has-text-grey">
|
||||
<strong>{{or .Owner.Username "[unavailable]"}}</strong>
|
||||
</a>
|
||||
{{if .Category}}
|
||||
by <a href="/f/{{.Fragment}}">{{PrettyTitle}}</a>
|
||||
{{else}}
|
||||
[unavailable]
|
||||
by
|
||||
{{template "avatar-16x16" .Owner}}
|
||||
{{if .Owner.Username}}
|
||||
<a href="/u/{{.Owner.Username}}" class="has-text-grey">
|
||||
<strong>{{or .Owner.Username "[unavailable]"}}</strong>
|
||||
</a>
|
||||
{{else}}
|
||||
[unavailable]
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{$FollowerCount := $Root.FollowersMap.Get .ID}}
|
||||
{{if $FollowerCount}}
|
||||
<span class="has-text-success ml-2" title="This forum is followed by {{$FollowerCount}} member{{Pluralize64 $FollowerCount}}.">
|
||||
<i class="fa fa-book-bookmark mr-1"></i> {{$FollowerCount}}
|
||||
</span>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
<div class="column py-1">
|
||||
|
|
Loading…
Reference in New Issue
Block a user