render.gno

5.61 Kb ยท 201 lines
  1package social_feed
  2
  3import (
  4	"chain/runtime"
  5	"strconv"
  6	"strings"
  7
  8	"gno.land/p/moul/md"
  9	"gno.land/p/nt/mux"
 10	"gno.land/p/nt/seqid"
 11	"gno.land/p/nt/ufmt"
 12	ma "gno.land/p/zenao/multiaddr"
 13
 14	feedsv1 "gno.land/p/zenao/feeds/v1"
 15)
 16
 17var router *mux.Router
 18
 19const SocialFeedPagePath = ""
 20const DetailSocialFeedPagePath = "{id}"
 21const DetailPostPagePath = "post/{postId}"
 22
 23func init() {
 24	router = mux.NewRouter()
 25	router.HandleFunc(SocialFeedPagePath, SocialFeedPageHandler)
 26	router.HandleFunc(DetailSocialFeedPagePath, DetailSocialFeedPageHandler)
 27	router.HandleFunc(DetailPostPagePath, DetailPostPageHandler)
 28}
 29
 30func Render(path string) string {
 31	return router.Render(path)
 32}
 33
 34func SocialFeedPageHandler(res *mux.ResponseWriter, req *mux.Request) {
 35	res.Write(md.H1("Welcome to the social feed factory ๐Ÿญ"))
 36	res.Write(md.Paragraph("A social feed does not need to be created explicitly, but have to if you want to add auth layer."))
 37	res.Write(md.Paragraph("Here are the 20 first social feeds:"))
 38	i := 0
 39	feeds.Iterate("", "", func(key string, feedRaw interface{}) bool {
 40		res.Write(md.BulletItem(md.Link(key, "social_feed:"+key)))
 41		i++
 42		if i >= 20 {
 43			return true
 44		}
 45		return false
 46	})
 47	res.Write(md.Blockquote("You can see any feed by adding :id to the URL"))
 48}
 49
 50func DetailSocialFeedPageHandler(res *mux.ResponseWriter, req *mux.Request) {
 51	id := req.GetVar("id")
 52	feedRaw, ok := feeds.Get(id)
 53	if !ok {
 54		res.Write(md.H1("Feed not found"))
 55		return
 56	}
 57	feed := feedRaw.(*Feed)
 58	res.Write(md.H1(id))
 59	if feed.CrossNetwork {
 60		res.Write(md.Paragraph("Cross network feed โœ…"))
 61	} else {
 62		res.Write(md.Paragraph("Local feed โœ…"))
 63	}
 64	if feed.AuthFunc != nil {
 65		res.Write(md.Paragraph("This feed is private ๐Ÿ”’"))
 66	} else {
 67		res.Write(md.Paragraph("This feed is public ๐Ÿ”“"))
 68	}
 69	res.Write(md.Paragraph(md.Bold("Posts:")))
 70	res.Write(md.HorizontalRule())
 71	i := 0
 72	start := id + ":"
 73	end := id + ";"
 74	postsByFeed.ReverseIterate(start, end, func(key string, postRaw interface{}) bool {
 75		post := postRaw.(*feedsv1.Post)
 76		if post.ParentUri != "" {
 77			return false
 78		}
 79		res.Write(renderZenaoPost(post))
 80		if i >= 20 {
 81			return true
 82		}
 83		res.Write(md.HorizontalRule())
 84		i++
 85		return false
 86	})
 87}
 88
 89func DetailPostPageHandler(res *mux.ResponseWriter, req *mux.Request) {
 90	postId := req.GetVar("postId")
 91	postRaw, ok := posts.Get(postId)
 92	if !ok {
 93		res.Write(md.H1("Post not found"))
 94		return
 95	}
 96	post := postRaw.(*feedsv1.Post)
 97	res.Write(md.H1(ufmt.Sprintf("Post #%d", post.LocalPostId)))
 98	res.Write(renderZenaoPost(post))
 99	res.Write(md.H2("Comments:"))
100	res.Write(md.HorizontalRule())
101	comments := GetChildrenPosts(ufmt.Sprintf("%d", post.LocalPostId), 0, 20, "", "")
102	for _, comment := range comments {
103		res.Write(renderZenaoPost(comment.Post))
104		res.Write(md.HorizontalRule())
105	}
106}
107
108func renderZenaoPost(post *feedsv1.Post) string {
109	pkgPath := runtime.CurrentRealm().PkgPath()
110	linkPath := getLinkPath(pkgPath)
111	var s string
112	switch ext := post.Post.(type) {
113	case *feedsv1.StandardPost:
114		s = renderStandardPost(ext)
115	case *feedsv1.ArticlePost:
116		s = renderArticlePost(ext)
117	case *feedsv1.LinkPost:
118		s = renderLinkPost(ext)
119	case *feedsv1.ImagePost:
120		s = renderImagePost(ext)
121	case *feedsv1.VideoPost:
122		s = renderVideoPost(ext)
123	default:
124		s = md.Paragraph("Unknown post type")
125	}
126	s += md.Paragraph(md.Blockquote("Author: " + post.Author))
127	s += md.Link("See details", linkPath+":post/"+seqid.ID(post.LocalPostId).String())
128	commentCount := CountChildren(post.LocalPostId)
129	s += md.Paragraph("NUMBER OF COMMENTS: " + ufmt.Sprintf("%d", commentCount) + " " + md.Link("see comments", linkPath+":post/"+seqid.ID(post.LocalPostId).String()))
130	reactions := GetPostReactions(post.LocalPostId, "")
131	s += md.Paragraph("NUMBER OF REACTIONS: " + ufmt.Sprintf("%d", len(reactions)))
132	if len(reactions) > 0 {
133		s += md.Paragraph("Reactions:")
134		for _, reaction := range reactions {
135			s += md.BulletItem(reaction.Icon + ": " + md.Bold(ufmt.Sprintf("%d", reaction.Count)))
136		}
137	}
138
139	return s
140}
141
142func renderStandardPost(ext *feedsv1.StandardPost) string {
143	return md.H3("POST KIND: STANDARD") + md.Paragraph(ext.Content)
144}
145
146func renderArticlePost(ext *feedsv1.ArticlePost) string {
147	return md.H3("POST KIND: ARTICLE") + md.H3(ext.Title) + md.Paragraph(ext.PreviewText) + md.Image("article preview", ext.PreviewImageUri) + md.Paragraph(ext.Content)
148}
149
150func renderLinkPost(ext *feedsv1.LinkPost) string {
151	return md.H3("POST KIND: LINK") + md.Link(ext.Uri, handleMultiaddrLink(ext.Uri))
152}
153
154func renderImagePost(ext *feedsv1.ImagePost) string {
155	return md.H3("POST KIND: IMAGE") + md.Image("image", ext.ImageUri) + md.Paragraph(ext.Description)
156}
157
158func renderVideoPost(ext *feedsv1.VideoPost) string {
159	return md.H3("POST KIND: VIDEO") + md.Link(ext.VideoUri, ext.VideoUri) + md.Image("video thumbnail", ext.ThumbnailImageUri) + md.Paragraph(ext.Description)
160}
161
162// XXX: adapt it to many different protocols
163func handleMultiaddrLink(link string) string {
164	ma, err := ma.NewMultiaddr(Protocols, link)
165	if err != nil {
166		return ""
167	}
168	p, err := Protocols.ProtocolWithName("gno")
169	if err != nil {
170		return ""
171	}
172	gnoPath, err := ma.ValueForProtocol(p.Code)
173	if err != nil {
174		return ""
175	}
176	poll, err := Protocols.ProtocolWithName("poll")
177	if err != nil {
178		return ""
179	}
180	pollID, err := ma.ValueForProtocol(poll.Code)
181	if err != nil {
182		return ""
183	}
184	pollIDint, err := strconv.ParseInt(pollID, 10, 64)
185	if err != nil {
186		return ""
187	}
188	slashIdx := strings.LastIndex(gnoPath, "/")
189	if slashIdx == -1 {
190		return ""
191	}
192	return "./" + gnoPath[slashIdx+1:] + ":" + seqid.ID(pollIDint).String()
193}
194
195func getLinkPath(pkgPath string) string {
196	slashIdx := strings.IndexRune(pkgPath, '/')
197	if slashIdx != 1 {
198		return pkgPath[slashIdx:]
199	}
200	return ""
201}