posts.gno

4.38 Kb ยท 187 lines
  1package social_feed
  2
  3import (
  4	"strconv"
  5	"time"
  6
  7	"gno.land/p/nt/seqid"
  8
  9	feedsv1 "gno.land/p/zenao/feeds/v1"
 10	"gno.land/p/zenao/realmid"
 11)
 12
 13func NewPost(_ realm, feedId string, post *feedsv1.Post) uint64 {
 14	assertActive()
 15
 16	localPostId := id.Next()
 17
 18	var creator string
 19	if feedRaw, ok := feeds.Get(feedId); ok {
 20		// local feed, can be protected
 21		feed := feedRaw.(*Feed)
 22		creator = feed.auth()
 23		feedsByPost.Set(localPostId.String(), feed)
 24	} else {
 25		creator = realmid.Previous()
 26	}
 27
 28	post.Author = creator
 29	post.CreatedAt = time.Now().Unix()
 30	post.UpdatedAt = time.Now().Unix()
 31	post.DeletedAt = 0
 32	post.LocalPostId = uint64(localPostId)
 33
 34	feedKey := feedId + ":" + localPostId.String()
 35	posts.Set(localPostId.String(), post)
 36	postsByFeed.Set(feedKey, post)
 37
 38	if post.ParentUri != "" {
 39		parentIdInt, err := strconv.ParseUint(post.ParentUri, 10, 64)
 40		if err != nil {
 41			panic("parentId is not a valid uint64")
 42		}
 43		postsByParent.Set(seqid.ID(parentIdInt).String()+":"+localPostId.String(), post)
 44	}
 45	return post.LocalPostId
 46}
 47
 48func GetPost(localPostId uint64) *feedsv1.Post {
 49	localPostIdStr := seqid.ID(localPostId).String()
 50	postRaw, ok := posts.Get(localPostIdStr)
 51	if !ok {
 52		panic("post not found")
 53	}
 54	return postRaw.(*feedsv1.Post)
 55}
 56
 57func DeletePost(_ realm, localPostId uint64) {
 58	assertActive()
 59
 60	localPostIdStr := seqid.ID(localPostId).String()
 61	postRaw, ok := posts.Get(localPostIdStr)
 62	if !ok {
 63		panic("post not found")
 64	}
 65
 66	var caller string
 67	if feedRaw, ok := feedsByPost.Get(localPostIdStr); ok {
 68		caller = feedRaw.(*Feed).auth()
 69	} else {
 70		caller = realmid.Previous()
 71	}
 72
 73	post := postRaw.(*feedsv1.Post)
 74	if post.Author != caller {
 75		panic("you are not the author of this post")
 76	}
 77	if post.DeletedAt != 0 {
 78		panic("post is deleted")
 79	}
 80	// XXX: should we just remove the post from the feed & all trees ?
 81	post.DeletedAt = time.Now().Unix()
 82}
 83
 84func EditPost(_ realm, localPostId uint64, newPost *feedsv1.Post) {
 85	assertActive()
 86
 87	localPostIdStr := seqid.ID(localPostId).String()
 88	postRaw, ok := posts.Get(localPostIdStr)
 89	if !ok {
 90		panic("post not found")
 91	}
 92
 93	var caller string
 94	if feedRaw, ok := feedsByPost.Get(localPostIdStr); ok {
 95		caller = feedRaw.(*Feed).auth()
 96	} else {
 97		caller = realmid.Previous()
 98	}
 99
100	post := postRaw.(*feedsv1.Post)
101	if post.Author != caller {
102		panic("you are not the author of this post")
103	}
104	if post.DeletedAt != 0 {
105		panic("post is deleted")
106	}
107
108	//XXX: this is a hack to force these fields to be same as the original post
109	newPost.LocalPostId = post.LocalPostId
110	newPost.CreatedAt = post.CreatedAt
111	newPost.Author = post.Author
112	newPost.ParentUri = post.ParentUri
113	newPost.UpdatedAt = time.Now().Unix()
114	*post = *newPost
115}
116
117func ReactPost(_ realm, localPostId uint64, icon string) {
118	assertActive()
119
120	localPostIdStr := seqid.ID(localPostId).String()
121
122	var caller string
123	if feedRaw, ok := feedsByPost.Get(localPostIdStr); ok {
124		caller = feedRaw.(*Feed).auth()
125	} else {
126		caller = realmid.Previous()
127	}
128
129	userReactId := localPostIdStr + ":" + icon + ":" + caller
130	reactId := localPostIdStr + ":" + icon
131
132	if userReactions.Has(userReactId) {
133		userReactions.Remove(userReactId)
134
135		count := uint32(1)
136		if reactions.Has(reactId) {
137			countRaw, _ := reactions.Get(reactId)
138			count = countRaw.(uint32)
139			if count > 1 {
140				reactions.Set(reactId, count-1)
141			} else {
142				reactions.Remove(reactId)
143			}
144		}
145	} else {
146		userReactions.Set(userReactId, struct{}{})
147
148		count := uint32(0)
149		if reactions.Has(reactId) {
150			countRaw, _ := reactions.Get(reactId)
151			count = countRaw.(uint32)
152		}
153		reactions.Set(reactId, count+1)
154	}
155}
156
157func GetPostReactions(localPostId uint64, user string) []*feedsv1.ReactionView {
158	localPostIdStr := seqid.ID(localPostId).String()
159	var res []*feedsv1.ReactionView
160	start := localPostIdStr + ":"
161	end := localPostIdStr + ";"
162	reactions.Iterate(start, end, func(key string, value interface{}) bool {
163		icon := key[len(localPostIdStr)+1:]
164		count := value.(uint32)
165
166		userReactKey := localPostIdStr + ":" + icon + ":" + user
167		userHasVoted := userReactions.Has(userReactKey)
168
169		react := &feedsv1.ReactionView{
170			Icon:         icon,
171			Count:        count,
172			UserHasVoted: userHasVoted,
173		}
174		res = append(res, react)
175		return false
176	})
177	return res
178
179}
180
181func GetPostView(localPostId uint64, user string) *feedsv1.PostView {
182	return &feedsv1.PostView{
183		Post:          GetPost(localPostId),
184		Reactions:     GetPostReactions(localPostId, user),
185		ChildrenCount: CountChildren(localPostId),
186	}
187}