website/pkg/models/forum_membership.go
Noah Petherbridge eb85b2e090 Forum Memberships & My List
* Add "Browse" tab to the forums to view them all.
  * Text search
  * Show all, official, community, or "My List" forums.
* Add a Follow/Unfollow button into the header bar of forums to add it to
  "My List"
* On the Categories page, a special "My List" category appears at the top
  if the user follows categories, with their follows in alphabetical order.
* On the Categories & Browse pages: forums you follow will have a green
  bookmark icon by their name.

Permissions:

* The forum owner is able to Delete comments by others, but not Edit.

Notes:

* Currently a max limit of 100 follow forums (no pagination yet).
2024-08-20 21:26:53 -07:00

122 lines
2.9 KiB
Go

package models
import (
"strings"
"time"
"code.nonshy.com/nonshy/website/pkg/log"
"gorm.io/gorm"
)
// ForumMembership table.
type ForumMembership struct {
ID uint64 `gorm:"primaryKey"`
UserID uint64 `gorm:"index"`
User User `gorm:"foreignKey:user_id"`
ForumID uint64 `gorm:"index"`
Forum Forum `gorm:"foreignKey:forum_id"`
Approved bool `gorm:"index"`
IsModerator bool `gorm:"index"`
CreatedAt time.Time
UpdatedAt time.Time
}
// Preload related tables for the forum (classmethod).
func (f *ForumMembership) Preload() *gorm.DB {
return DB.Preload("User").Preload("Forum")
}
// CreateForumMembership subscribes the user to a forum.
func CreateForumMembership(user *User, forum *Forum) (*ForumMembership, error) {
var (
f = &ForumMembership{
User: *user,
Forum: *forum,
Approved: true,
}
result = DB.Create(f)
)
return f, result.Error
}
// GetForumMembership looks up a forum membership.
func GetForumMembership(user *User, forum *Forum) (*ForumMembership, error) {
var (
f = &ForumMembership{}
result = f.Preload().Where(
"user_id = ? AND forum_id = ?",
user.ID, forum.ID,
).First(&f)
)
return f, result.Error
}
// IsForumSubscribed checks if the current user subscribes to this forum.
func IsForumSubscribed(user *User, forum *Forum) bool {
f, _ := GetForumMembership(user, forum)
return f.UserID == user.ID
}
// Delete a forum membership.
func (f *ForumMembership) Delete() error {
return DB.Delete(f).Error
}
// PaginateForumMemberships paginates over a user's ForumMemberships.
func PaginateForumMemberships(user *User, pager *Pagination) ([]*ForumMembership, error) {
var (
fs = []*ForumMembership{}
query = (&ForumMembership{}).Preload()
wheres = []string{}
placeholders = []interface{}{}
)
query = query.Where(
strings.Join(wheres, " AND "),
placeholders...,
).Order(pager.Sort)
query.Model(&ForumMembership{}).Count(&pager.Total)
result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&fs)
return fs, result.Error
}
// ForumMembershipMap maps table IDs to Likes metadata.
type ForumMembershipMap map[uint64]bool
// Get like stats from the map.
func (fm ForumMembershipMap) Get(id uint64) bool {
return fm[id]
}
// MapForumMemberships looks up a user's memberships in bulk.
func MapForumMemberships(user *User, forums []*Forum) ForumMembershipMap {
var (
result = ForumMembershipMap{}
forumIDs = []uint64{}
)
// Initialize the result set.
for _, forum := range forums {
result[forum.ID] = false
forumIDs = append(forumIDs, forum.ID)
}
// Map the forum IDs the user subscribes to.
var followIDs = []uint64{}
if res := DB.Model(&ForumMembership{}).Select(
"forum_id",
).Where(
"user_id = ? AND forum_id IN ?",
user.ID, forumIDs,
).Scan(&followIDs); res.Error != nil {
log.Error("MapForumMemberships: %s", res.Error)
}
for _, forumID := range followIDs {
result[forumID] = true
}
return result
}