Search Apps Documentation Source Content File Folder Download Copy Actions Download

render.gno

1.71 Kb · 72 lines
 1package memba_appstore_v1
 2
 3import (
 4	"chain/runtime"
 5
 6	"gno.land/p/nt/ufmt/v0"
 7)
 8
 9// RenderMaxRows bounds the Render() output so a large catalog can never make the
10// realm render-DoS (mirrors the feed's live-only bounded render).
11const RenderMaxRows = 100
12
13// Render shows the live catalog (bounded) — a read-only trust surface for gnoweb.
14// The frontend reads structured data via the getters, not this markdown.
15func Render(path string) string {
16	sb := ""
17	sb += "# Memba App Store\n\n"
18	sb += ufmt.Sprintf("Curated gno.land dApps. **Listing fee:** %d ugnot → treasury.\n\n", registrationFee)
19	if paused {
20		sb += "> ⏸️ Registration is paused.\n\n"
21	}
22
23	rows := 0
24	live := 0
25	sb += "| App | Package | Category |\n|---|---|---|\n"
26	listings.Iterate("", "", func(_ string, v any) bool {
27		l := v.(*Listing)
28		if !isVisible(l) {
29			return false
30		}
31		live++
32		if rows >= RenderMaxRows {
33			return false // stop appending, keep counting via the header below
34		}
35		rows++
36		sb += ufmt.Sprintf("| %s | `%s` | %s |\n", esc(l.Name), esc(l.PkgPath), esc(l.Category))
37		return false
38	})
39
40	if live == 0 {
41		sb += "\n*No live apps yet.*\n"
42	} else {
43		sb += ufmt.Sprintf("\n*%d live app(s)%s. Block %d.*\n", live, moreNote(live), runtime.ChainHeight())
44	}
45	return sb
46}
47
48func moreNote(live int) string {
49	if live > RenderMaxRows {
50		return ufmt.Sprintf(" (showing first %d)", RenderMaxRows)
51	}
52	return ""
53}
54
55// esc neutralizes the markdown/table metachars that could break the table or inject
56// layout from an attacker-supplied listing field.
57func esc(s string) string {
58	out := ""
59	for _, r := range s {
60		switch r {
61		case '|':
62			out += "\\|"
63		case '\n', '\r':
64			out += " "
65		case '`':
66			out += "'"
67		default:
68			out += string(r)
69		}
70	}
71	return out
72}