render.gno
5.43 Kb · 144 lines
1package directory
2
3import (
4 "net/url"
5 "strconv"
6 "strings"
7
8 "gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/core/v1"
9 "gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/remixlab/render/v1"
10)
11
12// Render serves the native pages: overview, accepted entries, a single
13// entry, the review queue, members and rules.
14func Render(path string) string {
15 if members == nil {
16 return render.Heading(1, appTitle) + "This app is not initialized.\n"
17 }
18 segs, q := render.ParsePath(path)
19 switch {
20 case len(segs) == 0:
21 return renderHome()
22 case segs[0] == "entries" && len(segs) == 1:
23 return renderList("Entries", "entries", core.SubmissionAccepted, q)
24 case segs[0] == "entries" && len(segs) == 2:
25 id, err := strconv.ParseUint(segs[1], 10, 64)
26 if err != nil {
27 return notFound()
28 }
29 return renderEntry(id)
30 case segs[0] == "queue" && len(segs) == 1:
31 return renderList("Review queue", "queue", core.SubmissionSubmitted, q)
32 case segs[0] == "members" && len(segs) == 1:
33 return renderMembers(render.Page(q, render.PageCount(members.Size(), pageSize)))
34 case segs[0] == "rules" && len(segs) == 1:
35 return renderRules()
36 }
37 return notFound()
38}
39
40func renderHome() string {
41 accepted := entries.CountByStatus(core.SubmissionAccepted)
42 waiting := entries.CountByStatus(core.SubmissionSubmitted)
43 var sb strings.Builder
44 sb.WriteString(render.Heading(1, appTitle))
45 sb.WriteString("Entries are submitted for review; accepted entries are listed here. Every submission, including rejected ones, is public chain data.\n\n")
46 sb.WriteString("- " + render.Link("Entries", render.RealmURL(pkgPath, "entries")) + " (" + strconv.Itoa(accepted) + ")\n")
47 sb.WriteString("- " + render.Link("Review queue", render.RealmURL(pkgPath, "queue")) + " (" + strconv.Itoa(waiting) + ")\n")
48 sb.WriteString("- " + render.Link("Members", render.RealmURL(pkgPath, "members")) + " (" + strconv.Itoa(members.Size()) + ")\n")
49 sb.WriteString("- " + render.Link("Rules", render.RealmURL(pkgPath, "rules")) + "\n\n")
50 sb.WriteString(render.TxLink("Submit an entry", pkgPath, "Submit") + " · " + render.TxLink("Join (requires an invitation)", pkgPath, "Join") + "\n\n")
51 sb.WriteString(renderTable(entries.Submissions(int(core.SubmissionAccepted), 0, 5), false))
52 sb.WriteString("_Recipe " + render.Text(recipeID+"@"+recipeVersion) + ". Membership is address-based; one address is not one person._\n")
53 return sb.String()
54}
55
56func renderList(heading, route string, status core.SubmissionStatus, q url.Values) string {
57 pages := render.PageCount(entries.CountByStatus(status), pageSize)
58 page := render.Page(q, pages)
59 list := entries.Submissions(int(status), (page-1)*pageSize, pageSize)
60 return render.Heading(1, heading) +
61 renderTable(list, status == core.SubmissionSubmitted) +
62 render.Pager(pkgPath, route, page, pages)
63}
64
65func renderTable(list []core.SubmissionView, reviewActions bool) string {
66 if len(list) == 0 {
67 return "Nothing here yet.\n\n"
68 }
69 var rows [][]string
70 for _, v := range list {
71 idStr := strconv.FormatUint(v.ID, 10)
72 row := []string{
73 render.Link("#"+idStr, render.RealmURL(pkgPath, "entries/"+idStr)),
74 render.Text(v.Answers[titleIndex]),
75 }
76 if reviewActions {
77 row = append(row, reviewLinks(idStr))
78 }
79 rows = append(rows, render.TrustedRow(row...))
80 }
81 headers := []string{"Entry", "Title"}
82 if reviewActions {
83 headers = append(headers, "Review")
84 }
85 return render.Table(headers, rows)
86}
87
88func renderEntry(id uint64) string {
89 v, ok := entries.Submission(id)
90 if !ok {
91 return notFound()
92 }
93 idStr := strconv.FormatUint(id, 10)
94 if v.Status == core.SubmissionRejected {
95 return render.Heading(1, "Entry #"+idStr) + "This entry was rejected.\n\n" +
96 render.Link("All entries", render.RealmURL(pkgPath, "entries")) + "\n"
97 }
98 var sb strings.Builder
99 sb.WriteString(render.Heading(1, v.Answers[titleIndex]))
100 sb.WriteString("Status: **" + v.Status.String() + "** · submitted by " + render.Code(v.Submitter.String()) + "\n\n")
101 var rows [][]string
102 for i, f := range entries.Fields() {
103 if i != titleIndex && v.Answers[i] != "" {
104 rows = append(rows, render.Row(f.Label, v.Answers[i]))
105 }
106 }
107 if len(rows) > 0 {
108 sb.WriteString(render.Table([]string{"Field", "Answer"}, rows))
109 }
110 if v.Status == core.SubmissionSubmitted {
111 sb.WriteString("Awaiting review: " + reviewLinks(idStr) + "\n\n")
112 }
113 sb.WriteString(render.Link("All entries", render.RealmURL(pkgPath, "entries")) + "\n")
114 return sb.String()
115}
116
117func reviewLinks(idStr string) string {
118 return render.TxLink("accept", pkgPath, "Accept", "id", idStr) + " · " +
119 render.TxLink("reject", pkgPath, "Reject", "id", idStr)
120}
121
122func renderMembers(page int) string {
123 var rows [][]string
124 for _, m := range members.List((page-1)*pageSize, pageSize) {
125 rows = append(rows, render.Row(m.Address.String(), m.Role.String()))
126 }
127 return render.Heading(1, "Members") +
128 render.Table([]string{"Address", "Role"}, rows) +
129 render.Pager(pkgPath, "members", page, render.PageCount(members.Size(), pageSize))
130}
131
132func renderRules() string {
133 var rows [][]string
134 for _, r := range policy.List() {
135 rows = append(rows, render.Row(r.Op, r.Rule.String(), r.Floor.String()))
136 }
137 return render.Heading(1, "Rules") +
138 render.Table([]string{"Operation", "Who may", "Most permissive allowed"}, rows) +
139 "Up to 100 pending or accepted submissions, at most 3 per address. Review decisions are final; rejecting an entry frees its slot." + "\n"
140}
141
142func notFound() string {
143 return render.Heading(1, "Not found") + render.Link("Back to the app", render.RealmURL(pkgPath, "")) + "\n"
144}