Reaction info

This commit is contained in:
LittleSheep 2024-03-03 22:57:17 +08:00
parent 1725724758
commit 932bdf1e5a
10 changed files with 187 additions and 46 deletions

View File

@ -15,26 +15,26 @@ type Article struct {
Comments []Comment `json:"comments" gorm:"foreignKey:ArticleID"`
}
func (p Article) GetReplyTo() PostInterface {
func (p *Article) GetReplyTo() PostInterface {
return nil
}
func (p Article) GetRepostTo() PostInterface {
func (p *Article) GetRepostTo() PostInterface {
return nil
}
func (p Article) GetHashtags() []Tag {
func (p *Article) GetHashtags() []Tag {
return p.Hashtags
}
func (p Article) GetCategories() []Category {
func (p *Article) GetCategories() []Category {
return p.Categories
}
func (p Article) SetHashtags(tags []Tag) {
func (p *Article) SetHashtags(tags []Tag) {
p.Hashtags = tags
}
func (p Article) SetCategories(categories []Category) {
func (p *Article) SetCategories(categories []Category) {
p.Categories = categories
}

View File

@ -16,22 +16,22 @@ type Comment struct {
Moment *Moment `json:"moment"`
}
func (p Comment) GetReplyTo() PostInterface {
func (p *Comment) GetReplyTo() PostInterface {
return p.ReplyTo
}
func (p Comment) GetHashtags() []Tag {
func (p *Comment) GetHashtags() []Tag {
return p.Hashtags
}
func (p Comment) GetCategories() []Category {
func (p *Comment) GetCategories() []Category {
return p.Categories
}
func (p Comment) SetHashtags(tags []Tag) {
func (p *Comment) SetHashtags(tags []Tag) {
p.Hashtags = tags
}
func (p Comment) SetCategories(categories []Category) {
func (p *Comment) SetCategories(categories []Category) {
p.Categories = categories
}

View File

@ -15,26 +15,26 @@ type Moment struct {
Comments []Comment `json:"comments" gorm:"foreignKey:MomentID"`
}
func (p Moment) GetRepostTo() PostInterface {
func (p *Moment) GetRepostTo() PostInterface {
return p.RepostTo
}
func (p Moment) GetRealm() *Realm {
func (p *Moment) GetRealm() *Realm {
return p.Realm
}
func (p Moment) GetHashtags() []Tag {
func (p *Moment) GetHashtags() []Tag {
return p.Hashtags
}
func (p Moment) GetCategories() []Category {
func (p *Moment) GetCategories() []Category {
return p.Categories
}
func (p Moment) SetHashtags(tags []Tag) {
func (p *Moment) SetHashtags(tags []Tag) {
p.Hashtags = tags
}
func (p Moment) SetCategories(categories []Category) {
func (p *Moment) SetCategories(categories []Category) {
p.Categories = categories
}

View File

@ -22,29 +22,34 @@ type PostBase struct {
AuthorID uint `json:"author_id"`
Author Account `json:"author"`
// TODO Give the reactions & replies & reposts info back
// Dynamic Calculated Values
ReactionList map[string]int64 `json:"reaction_list" gorm:"-"`
}
func (p PostBase) GetID() uint {
func (p *PostBase) GetID() uint {
return p.ID
}
func (p PostBase) GetReplyTo() PostInterface {
func (p *PostBase) GetReplyTo() PostInterface {
return nil
}
func (p PostBase) GetRepostTo() PostInterface {
func (p *PostBase) GetRepostTo() PostInterface {
return nil
}
func (p PostBase) GetAuthor() Account {
func (p *PostBase) GetAuthor() Account {
return p.Author
}
func (p PostBase) GetRealm() *Realm {
func (p *PostBase) GetRealm() *Realm {
return nil
}
func (p *PostBase) SetReactionList(list map[string]int64) {
p.ReactionList = list
}
type PostInterface interface {
GetID() uint
GetHashtags() []Tag
@ -56,4 +61,5 @@ type PostInterface interface {
SetHashtags([]Tag)
SetCategories([]Category)
SetReactionList(map[string]int64)
}

View File

@ -12,8 +12,8 @@ import (
"github.com/samber/lo"
)
func contextArticle() *services.PostTypeContext[models.Article] {
return &services.PostTypeContext[models.Article]{
func contextArticle() *services.PostTypeContext[*models.Article] {
return &services.PostTypeContext[*models.Article]{
Tx: database.C,
TypeName: "Article",
CanReply: false,
@ -31,6 +31,11 @@ func getArticle(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
item.ReactionList, err = mx.CountReactions(item.ID)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.JSON(item)
}
@ -102,7 +107,7 @@ func createArticle(c *fiber.Ctx) error {
mx := contextArticle()
item := models.Article{
item := &models.Article{
PostBase: models.PostBase{
Alias: data.Alias,
Attachments: data.Attachments,
@ -192,7 +197,7 @@ func reactArticle(c *fiber.Ctx) error {
mx := contextArticle()
item, err := mx.Get(uint(id))
item, err := mx.Get(uint(id), true)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
@ -218,7 +223,7 @@ func deleteArticle(c *fiber.Ctx) error {
mx := contextArticle().FilterAuthor(user.ID)
item, err := mx.Get(uint(id))
item, err := mx.Get(uint(id), true)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}

View File

@ -13,8 +13,8 @@ import (
"github.com/samber/lo"
)
func contextComment() *services.PostTypeContext[models.Comment] {
return &services.PostTypeContext[models.Comment]{
func contextComment() *services.PostTypeContext[*models.Comment] {
return &services.PostTypeContext[*models.Comment]{
Tx: database.C,
TypeName: "Comment",
CanReply: false,
@ -32,6 +32,11 @@ func getComment(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
item.ReactionList, err = mx.CountReactions(item.ID)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.JSON(item)
}
@ -103,7 +108,7 @@ func createComment(c *fiber.Ctx) error {
mx := contextComment()
item := models.Comment{
item := &models.Comment{
PostBase: models.PostBase{
Alias: data.Alias,
Attachments: data.Attachments,
@ -199,7 +204,7 @@ func reactComment(c *fiber.Ctx) error {
mx := contextComment()
item, err := mx.Get(uint(id))
item, err := mx.Get(uint(id), true)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
@ -225,7 +230,7 @@ func deleteComment(c *fiber.Ctx) error {
mx := contextComment().FilterAuthor(user.ID)
item, err := mx.Get(uint(id))
item, err := mx.Get(uint(id), true)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}

View File

@ -6,6 +6,7 @@ import (
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
"github.com/spf13/viper"
)
@ -23,6 +24,7 @@ type FeedItem struct {
RealmID *uint `json:"realm_id"`
Author models.Account `json:"author" gorm:"embedded"`
ReactionList map[string]int64 `json:"reaction_list"`
}
const (
@ -56,7 +58,7 @@ func listFeed(c *fiber.Ctx) error {
}
}
var result []FeedItem
var result []*FeedItem
userTable := viper.GetString("database.prefix") + "accounts"
commentTable := viper.GetString("database.prefix") + "comments"
@ -84,6 +86,59 @@ func listFeed(c *fiber.Ctx) error {
offset,
).Scan(&result)
if !c.QueryBool("noReact", false) {
var reactions []struct {
PostID uint
Symbol string
Count int64
}
revertReaction := func(dataset string) error {
itemMap := lo.SliceToMap(lo.FilterMap(result, func(item *FeedItem, index int) (*FeedItem, bool) {
return item, item.ModelType == dataset
}), func(item *FeedItem) (uint, *FeedItem) {
return item.ID, item
})
idx := lo.Map(lo.Filter(result, func(item *FeedItem, index int) bool {
return item.ModelType == dataset
}), func(item *FeedItem, index int) uint {
return item.ID
})
if err := database.C.Model(&models.Reaction{}).
Select(dataset+"_id as post_id, symbol, COUNT(id) as count").
Where(dataset+"_id IN (?)", idx).
Group("post_id, symbol").
Scan(&reactions).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
list := map[uint]map[string]int64{}
for _, info := range reactions {
if _, ok := list[info.PostID]; !ok {
list[info.PostID] = make(map[string]int64)
}
list[info.PostID][info.Symbol] = info.Count
}
for k, v := range list {
if post, ok := itemMap[k]; ok {
post.ReactionList = v
}
}
return nil
}
if err := revertReaction("article"); err != nil {
return err
}
if err := revertReaction("moment"); err != nil {
return err
}
}
var count int64
database.C.Raw(`SELECT COUNT(*) FROM (? UNION ALL ?) as feed`,
database.C.Select(queryArticle).Model(&models.Article{}),

View File

@ -12,8 +12,8 @@ import (
"github.com/samber/lo"
)
func contextMoment() *services.PostTypeContext[models.Moment] {
return &services.PostTypeContext[models.Moment]{
func contextMoment() *services.PostTypeContext[*models.Moment] {
return &services.PostTypeContext[*models.Moment]{
Tx: database.C,
TypeName: "Moment",
CanReply: false,
@ -31,6 +31,11 @@ func getMoment(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
item.ReactionList, err = mx.CountReactions(item.ID)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
return c.JSON(item)
}
@ -101,7 +106,7 @@ func createMoment(c *fiber.Ctx) error {
mx := contextMoment()
item := models.Moment{
item := &models.Moment{
PostBase: models.PostBase{
Alias: data.Alias,
Attachments: data.Attachments,
@ -197,7 +202,7 @@ func reactMoment(c *fiber.Ctx) error {
mx := contextMoment()
item, err := mx.Get(uint(id))
item, err := mx.Get(uint(id), true)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
@ -223,7 +228,7 @@ func deleteMoment(c *fiber.Ctx) error {
mx := contextMoment().FilterAuthor(user.ID)
item, err := mx.Get(uint(id))
item, err := mx.Get(uint(id), true)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}

View File

@ -1,5 +1,6 @@
package services
import "C"
import (
"code.smartsheep.studio/hydrogen/identity/pkg/grpc/proto"
"code.smartsheep.studio/hydrogen/interactive/pkg/database"
@ -33,12 +34,16 @@ func (v *PostTypeContext[T]) GetTableName(plural ...bool) string {
}
}
func (v *PostTypeContext[T]) Preload() *PostTypeContext[T] {
func (v *PostTypeContext[T]) Preload(noComments ...bool) *PostTypeContext[T] {
v.Tx.Preload("Author").
Preload("Attachments").
Preload("Categories").
Preload("Hashtags")
if len(noComments) <= 0 || !noComments[0] {
v.Tx = v.Tx.Preload("Comments")
}
if v.CanReply {
v.Tx.Preload("ReplyTo")
}
@ -98,18 +103,18 @@ func (v *PostTypeContext[T]) SortCreatedAt(order string) *PostTypeContext[T] {
return v
}
func (v *PostTypeContext[T]) GetViaAlias(alias string) (T, error) {
func (v *PostTypeContext[T]) GetViaAlias(alias string, noComments ...bool) (T, error) {
var item T
if err := v.Preload().Tx.Where("alias = ?", alias).First(&item).Error; err != nil {
if err := v.Preload(noComments...).Tx.Where("alias = ?", alias).First(&item).Error; err != nil {
return item, err
}
return item, nil
}
func (v *PostTypeContext[T]) Get(id uint) (T, error) {
func (v *PostTypeContext[T]) Get(id uint, noComments ...bool) (T, error) {
var item T
if err := v.Preload().Tx.Where("id = ?", id).First(&item).Error; err != nil {
if err := v.Preload(noComments...).Tx.Where("id = ?", id).First(&item).Error; err != nil {
return item, err
}
@ -126,6 +131,28 @@ func (v *PostTypeContext[T]) Count() (int64, error) {
return count, nil
}
func (v *PostTypeContext[T]) CountReactions(id uint) (map[string]int64, error) {
var reactions []struct {
Symbol string
Count int64
}
if err := database.C.Model(&models.Reaction{}).
Select("symbol, COUNT(id) as count").
Where(strings.ToLower(v.TypeName)+"_id = ?", id).
Group("symbol").
Scan(&reactions).Error; err != nil {
return map[string]int64{}, err
}
return lo.SliceToMap(reactions, func(item struct {
Symbol string
Count int64
}) (string, int64) {
return item.Symbol, item.Count
}), nil
}
func (v *PostTypeContext[T]) List(take int, offset int, noReact ...bool) ([]T, error) {
if take > 20 {
take = 20
@ -136,6 +163,44 @@ func (v *PostTypeContext[T]) List(take int, offset int, noReact ...bool) ([]T, e
return items, err
}
idx := lo.Map(items, func(item T, index int) uint {
return item.GetID()
})
if len(noReact) <= 0 || !noReact[0] {
var reactions []struct {
PostID uint
Symbol string
Count int64
}
if err := database.C.Model(&models.Reaction{}).
Select(strings.ToLower(v.TypeName)+"_id as post_id, symbol, COUNT(id) as count").
Where(strings.ToLower(v.TypeName)+"_id IN (?)", idx).
Group("post_id, symbol").
Scan(&reactions).Error; err != nil {
return items, err
}
itemMap := lo.SliceToMap(items, func(item T) (uint, T) {
return item.GetID(), item
})
list := map[uint]map[string]int64{}
for _, info := range reactions {
if _, ok := list[info.PostID]; !ok {
list[info.PostID] = make(map[string]int64)
}
list[info.PostID][info.Symbol] = info.Count
}
for k, v := range list {
if post, ok := itemMap[k]; ok {
post.SetReactionList(v)
}
}
}
return items, nil
}

View File

@ -30,7 +30,7 @@
open-on-hover
open-on-click
:open-delay="0"
:close-delay="1850"
:close-delay="0"
location="top"
transition="scroll-y-reverse-transition"
>