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}