render.gno
3.37 Kb · 107 lines
1package gnotif
2
3import (
4 "chain/runtime/unsafe"
5 "strconv"
6 "strings"
7
8 "gno.land/p/nt/avl/v0"
9 "gno.land/p/nt/markdown/sanitize/v0"
10)
11
12const pageSize = 50
13
14// Render serves "" (the first page), "page/N", "trigger/<id>" and
15// "target/<path>".
16func Render(path string) string {
17 switch {
18 case path == "":
19 return renderPage(1)
20 case strings.HasPrefix(path, "page/"):
21 n, err := strconv.Atoi(strings.TrimPrefix(path, "page/"))
22 if err != nil || n < 1 {
23 return "Not found."
24 }
25 return renderPage(n)
26 case strings.HasPrefix(path, "trigger/"):
27 v := triggers.Get(strings.TrimPrefix(path, "trigger/"))
28 if v == nil {
29 return "Not found."
30 }
31 return renderTrigger(v.(*Trigger))
32 case strings.HasPrefix(path, "target/"):
33 own := byTarget.Get(strings.TrimPrefix(path, "target/"))
34 if own == nil {
35 return "Not found."
36 }
37 return renderTarget(strings.TrimPrefix(path, "target/"), own.(*avl.Tree))
38 }
39 return "Not found."
40}
41
42// renderPage lists the triggers newest first, pageSize to a page.
43func renderPage(n int) string {
44 var b strings.Builder
45 b.WriteString("# gnotif triggers\n\n")
46 pages := (triggers.Size() + pageSize - 1) / pageSize
47 if n > pages {
48 b.WriteString("No triggers on this page.\n")
49 return b.String()
50 }
51 triggers.ReverseIterateByOffset((n-1)*pageSize, pageSize, func(_ string, v any) bool {
52 writeLine(&b, v.(*Trigger))
53 return false
54 })
55 if n < pages {
56 b.WriteString("\n[Next page](" + selfPath + ":page/" + strconv.Itoa(n+1) + ")\n")
57 }
58 return b.String()
59}
60
61// renderTarget lists a realm's verified triggers, newest first. The cap on
62// them keeps the page to one screen, so it is not paged.
63func renderTarget(target string, own *avl.Tree) string {
64 var b strings.Builder
65 b.WriteString("# gnotif triggers for " + sanitize.InlineText(target) + "\n\n")
66 own.ReverseIterate("", "", func(_ string, v any) bool {
67 writeLine(&b, v.(*Trigger))
68 return false
69 })
70 b.WriteString("\n[All triggers](" + selfPath + ")\n")
71 return b.String()
72}
73
74func writeLine(b *strings.Builder, tr *Trigger) {
75 b.WriteString("- [**" + tr.ID + "**](" + selfPath + ":trigger/" + tr.ID + ") " +
76 sanitize.InlineText(tr.Target) + " · " + sanitize.InlineText(tr.Event))
77 if tr.Param != "" {
78 b.WriteString(" · param " + sanitize.InlineText(tr.Param))
79 }
80 if tr.Verified {
81 b.WriteString(" · ✓ verified")
82 }
83 b.WriteString(" · by " + tr.Declarer.String() + "\n")
84}
85
86func renderTrigger(tr *Trigger) string {
87 var b strings.Builder
88 b.WriteString("# Trigger " + tr.ID + "\n\n")
89 b.WriteString("- Target: " + sanitize.InlineText(tr.Target) + "\n")
90 b.WriteString("- Event: " + sanitize.InlineText(tr.Event) + "\n")
91 b.WriteString("- Filter: " + sanitize.InlineText(tr.Filter) + "\n")
92 b.WriteString("- Param: " + sanitize.InlineText(tr.Param) + "\n")
93 b.WriteString("- Title: " + sanitize.InlineText(tr.Title) + "\n")
94 b.WriteString("- Body: " + sanitize.InlineText(tr.Body) + "\n")
95 b.WriteString("- Link: " + sanitize.InlineText(tr.Link) + "\n")
96 b.WriteString("- Declarer: " + tr.Declarer.String() + "\n")
97 if tr.Verified {
98 b.WriteString("- ✓ verified\n")
99 }
100 b.WriteString("\n[All triggers](" + selfPath + ")\n")
101 return b.String()
102}
103
104// selfPath is this realm's path on gnoweb. It is read at package init, where
105// the realm itself is current; Render is not crossing, so reading it there
106// would give the caller's path. It only builds links.
107var selfPath = strings.TrimPrefix(unsafe.CurrentRealm().PkgPath(), "gno.land")