Noah Petherbridge
bb08ec56ce
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.
146 lines
3.2 KiB
Go
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
|
|
}
|