Search Apps Documentation Source Content File Folder Download Copy Actions Download

render_post.gno

4.45 Kb · 180 lines
  1package boards2
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/gnoland/boards"
  8	"gno.land/p/moul/md"
  9)
 10
 11func renderPost(post *boards.Post, path, indent string, levels int) string {
 12	var b strings.Builder
 13
 14	// Thread reposts might not have a title, if so get title from source thread
 15	title := post.Title
 16	if boards.IsRepost(post) && title == "" {
 17		if board, ok := gBoards.Get(post.OriginalBoardID); ok {
 18			if src, ok := board.Threads.Get(post.ParentID); ok {
 19				title = src.Title
 20			}
 21		}
 22	}
 23
 24	if title != "" { // Replies don't have a title
 25		b.WriteString(md.H1(title))
 26	}
 27
 28	b.WriteString(indent + "\n")
 29	b.WriteString(renderPostContent(post, indent, levels))
 30
 31	if post.Replies.Size() == 0 {
 32		return b.String()
 33	}
 34
 35	// XXX: This triggers for reply views
 36	if levels == 0 {
 37		b.WriteString(indent + "\n")
 38		return b.String()
 39	}
 40
 41	if path != "" {
 42		b.WriteString(renderTopLevelReplies(post, path, indent, levels-1))
 43	} else {
 44		b.WriteString(renderSubReplies(post, indent, levels-1))
 45	}
 46	return b.String()
 47}
 48
 49func renderPostContent(post *boards.Post, indent string, levels int) string {
 50	var b strings.Builder
 51
 52	if post.Hidden {
 53		// Flagged comment should be hidden, but replies still visible (see: #3480)
 54		// Flagged threads will be hidden by render function caller.
 55		return indentBody(indent, md.Italic("⚠ Reply is hidden as it has been flagged as inappropriate")) + "\n"
 56	}
 57
 58	srcContent, srcPost := renderSourcePost(post, indent)
 59	if boards.IsRepost(post) && srcPost != nil {
 60		originLink := md.Link("another thread", makeThreadURI(srcPost))
 61		b.WriteString("  \nThis thread is a repost of " + originLink + ": \n")
 62	}
 63
 64	b.WriteString(srcContent)
 65
 66	if boards.IsRepost(post) && srcPost == nil && len(post.Body) > 0 {
 67		// Add a newline to separate source deleted message from repost body content
 68		b.WriteString("\n")
 69	}
 70
 71	b.WriteString(indentBody(indent, post.Body))
 72	b.WriteString("\n")
 73
 74	if boards.IsThread(post) {
 75		// Split content and controls for threads.
 76		b.WriteString("\n")
 77	}
 78
 79	// Buttons & counters
 80	b.WriteString(indent)
 81	if !boards.IsThread(post) {
 82		b.WriteString("  \n")
 83		b.WriteString(indent)
 84	}
 85
 86	creatorLink := userLink(post.Creator)
 87	date := post.CreatedAt.Format(dateFormat)
 88	b.WriteString("Created by " + creatorLink + " on " + date)
 89
 90	// Add a reply view link to each top level reply
 91	if !boards.IsThread(post) {
 92		b.WriteString(", " + md.Link("#"+post.ID.String(), makeReplyURI(post)))
 93	} else if post.Reposts.Size() > 0 { // Post is a thread
 94		b.WriteString(", " + strconv.Itoa(post.Reposts.Size()) + " repost(s)")
 95	}
 96
 97	b.WriteString("  \n")
 98
 99	actions := []string{
100		md.Link("Flag", makeFlagURI(post)),
101	}
102
103	if boards.IsThread(post) {
104		actions = append(actions, md.Link("Repost", makeCreateRepostURI(post)))
105	}
106
107	isReadonly := post.Readonly || post.Board.Readonly
108	if !isReadonly {
109		actions = append(
110			actions,
111			md.Link("Reply", makeCreateReplyURI(post)),
112			md.Link("Edit", makeEditPostURI(post)),
113			md.Link("Delete", makeDeletePostURI(post)),
114		)
115	}
116
117	if levels == 0 {
118		if boards.IsThread(post) {
119			actions = append(actions, md.Link("Show all Replies", makeThreadURI(post)))
120		} else {
121			actions = append(actions, md.Link("View Thread", makeThreadURI(post)))
122		}
123	}
124
125	b.WriteString(strings.Join(actions, " • ") + " \n")
126	return b.String()
127}
128
129func renderPostInner(post *boards.Post) string {
130	if boards.IsThread(post) {
131		return ""
132	}
133
134	var (
135		s         string
136		threadID  = post.ThreadID
137		thread, _ = post.Board.Threads.Get(threadID)
138	)
139
140	// Fully render parent if it's not a repost.
141	if !boards.IsRepost(post) {
142		parentID := post.ParentID
143		parent := thread
144
145		if thread.ID != parentID {
146			parent, _ = thread.Replies.Get(parentID)
147		}
148
149		s += renderPost(parent, "", "", 0) + "\n"
150	}
151
152	s += renderPost(post, "", "> ", 5)
153	return s
154}
155
156func renderSourcePost(post *boards.Post, indent string) (string, *boards.Post) {
157	if !boards.IsRepost(post) {
158		return "", nil
159	}
160
161	indent += "> "
162
163	// TODO: figure out a way to decouple posts from a global storage.
164	board, ok := gBoards.Get(post.OriginalBoardID)
165	if !ok {
166		// TODO: Boards can't be deleted so this might be redundant
167		return indentBody(indent, md.Italic("⚠ Source board has been deleted")+"\n"), nil
168	}
169
170	srcPost, ok := board.Threads.Get(post.ParentID)
171	if !ok {
172		return indentBody(indent, md.Italic("⚠ Source post has been deleted")+"\n"), nil
173	}
174
175	if srcPost.Hidden {
176		return indentBody(indent, md.Italic("⚠ Source post has been flagged as inappropriate")+"\n"), nil
177	}
178
179	return indentBody(indent, srcPost.Summary()) + "\n\n", srcPost
180}