website/pkg/models/like.go
Noah 93c13882aa Finish Forums + Likes & Notifications
Finish implementing the basic forum features:
* Pinned threads (admin or board owner only)
* Edit Thread settings when you edit the top-most comment.
* NoReply threads remove all the reply buttons.
* Explicit forums and threads are filtered out unless opted-in (admins
  always see them).
* Count the unique members who participated in each forum.
* Get the most recently updated thread to show on forum list page.
* Contact/Report page: handle receiving a comment ID to report on.

Implement Likes & Notifications
* Like buttons added to Photos and Profile Pages. Implemented via simple
  vanilla JS (likes.js) to make ajax requests to back-end to like/unlike.
* Notifications: for your photo or profile being liked. If you unlike,
  the existing notifications about the like are revoked.
* The notifications appear as an alert number in the nav bar and are read
  on the User Dashboard. Click to mark a notification as "read" or click
  the "mark all as read" button.

Update DeleteUser to scrub likes, notifications, threads, and comments.
2022-08-24 21:17:34 -07:00

146 lines
3.2 KiB
Go

package models
import (
"time"
"git.kirsle.net/apps/gosocial/pkg/log"
)
// Like table.
type Like struct {
ID uint64 `gorm:"primaryKey"`
UserID uint64 `gorm:"index"` // who it belongs to
TableName string
TableID uint64
CreatedAt time.Time
UpdatedAt time.Time
}
// LikeableTables are the set of table names that allow likes (used by the JSON API).
var LikeableTables = map[string]interface{}{
"photos": nil,
"users": nil,
}
// AddLike to something.
func AddLike(user *User, tableName string, tableID uint64) error {
// Already has a like?
var like = &Like{}
exist := DB.Model(like).Where(
"user_id = ? AND table_name = ? AND table_id = ?",
user.ID, tableName, tableID,
).First(&like)
if exist.Error == nil {
return nil
}
// Create it.
like = &Like{
UserID: user.ID,
TableName: tableName,
TableID: tableID,
}
return DB.Create(like).Error
}
// Unlike something.
func Unlike(user *User, tableName string, tableID uint64) error {
result := DB.Where(
"user_id = ? AND table_name = ? AND table_id = ?",
user.ID, tableName, tableID,
).Delete(&Like{})
return result.Error
}
// CountLikes on something.
func CountLikes(tableName string, tableID uint64) int64 {
var count int64
DB.Model(&Like{}).Where(
"table_name = ? AND table_id = ?",
tableName, tableID,
).Count(&count)
return count
}
// LikedIDs filters a set of table IDs to ones the user likes.
func LikedIDs(user *User, tableName string, tableIDs []uint64) ([]uint64, error) {
var result = []uint64{}
if r := DB.Table(
"likes",
).Select(
"table_id",
).Where(
"user_id = ? AND table_name = ? AND table_id IN ?",
user.ID, tableName, tableIDs,
).Scan(&result); r.Error != nil {
return result, r.Error
}
return result, nil
}
// LikeMap maps table IDs to Likes metadata.
type LikeMap map[uint64]*LikeStats
// Get like stats from the map.
func (lm LikeMap) Get(id uint64) *LikeStats {
if stats, ok := lm[id]; ok {
return stats
}
return &LikeStats{}
}
// LikeStats holds mapped statistics about liked objects.
type LikeStats struct {
Count int64 // how many total
UserLikes bool // current user likes it
}
// MapLikes over a set of table IDs.
func MapLikes(user *User, tableName string, tableIDs []uint64) LikeMap {
var result = LikeMap{}
// Initialize the result set.
for _, id := range tableIDs {
result[id] = &LikeStats{}
}
// Hold the result of the grouped count query.
type group struct {
ID uint64
Likes int64
}
var groups = []group{}
// Map the counts of likes to each of these IDs.
if res := DB.Table(
"likes",
).Select(
"table_id AS id, count(id) AS likes",
).Where(
"table_name = ? AND table_id IN ?",
tableName, tableIDs,
).Group("table_id").Scan(&groups); res.Error != nil {
log.Error("MapLikes: count query: %s", res.Error)
}
// Map the counts back in.
for _, row := range groups {
if stats, ok := result[row.ID]; ok {
stats.Count = row.Likes
}
}
// Does the CURRENT USER like any of these IDs?
if likedIDs, err := LikedIDs(user, tableName, tableIDs); err == nil {
log.Error("USER LIKES IDS: %+v", likedIDs)
for _, id := range likedIDs {
if stats, ok := result[id]; ok {
stats.UserLikes = true
}
}
}
return result
}