render.gno
5.13 Kb · 167 lines
1package avatar
2
3import (
4 "strconv"
5
6 "gno.land/p/moul/md/v0"
7 "gno.land/p/moul/mdtable/v0"
8 "gno.land/p/nt/grc721/v0"
9 "gno.land/p/nt/mdalert/v0"
10 "gno.land/p/nt/mux/v0"
11 settlers "gno.land/r/g17khqpukees4237dtn3astzapmp462vjhsz6st4/settlers/nft"
12)
13
14const (
15 // Sample size is the size in pixels of the avatars in the pages.
16 sampleSize = 48
17
18 // Samples is how many avatars the home page shows.
19 samples = 6
20)
21
22// Render renders the realm.
23//
24// By default renders what this realm is, how to use it, and some avatars.
25//
26// Paths:
27// - `svg/{address}`: The SVG of the Settler of an address.
28// - `md/{address}`: The Markdown that Markdown returns for an address, rendered, with the same parameters.
29func Render(path string) string {
30 router := mux.NewRouter()
31 router.NotFoundHandler = func(res *mux.ResponseWriter, _ *mux.Request) {
32 res.Write("Path not found")
33 }
34
35 router.HandleFunc("", renderHome)
36 router.HandleFunc("svg/{addr}", renderSVG)
37 router.HandleFunc("md/{addr}", renderMarkdown)
38
39 return router.Render(path)
40}
41
42func renderHome(res *mux.ResponseWriter, _ *mux.Request) {
43 res.Write(md.H1("Settler avatars"))
44
45 if Notice != "" {
46 res.Write(mdalert.New(NoticeType, "Message", Notice, false).String() + "\n\n")
47 }
48
49 res.Write(md.Paragraph("This realm draws a Settler as a picture give an address."))
50 res.Write(md.Paragraph(
51 "It doesn't own the Settler, or store anything about it, it just looks one up and draws it, " +
52 "fresh, every time it's asked. That's on purpose, keeping the drawing separate from the main " +
53 "collection means any other realm or web app on Gno.land, a forum, a leaderboard, a profile " +
54 "page, can show a Settler next to an address without needing to understand pixel art, traits, " +
55 "or how any of it's put together.",
56 ))
57
58 res.Write(md.Paragraph(
59 "The " + md.Link("Settlers nft", settlers.RealmPath) + " realm is still the one that actually mints " +
60 "the Settlers and keeps track of who owns what.",
61 ))
62
63 res.Write(md.Paragraph("Realm paths:"))
64 pathsTable := mdtable.Table{Headers: []string{"Path", "Description"}}
65 pathsTable.Append([]string{
66 md.InlineCode("svg/{address}"),
67 "The SVG of the Settler of an address.",
68 })
69 pathsTable.Append([]string{
70 md.InlineCode("md/{address}"),
71 "The Markdown that Markdown returns for an address, rendered, with the same parameters.",
72 })
73 res.Write(pathsTable.String() + "\n")
74
75 res.Write(md.H2("From a realm"))
76 res.Write(md.Paragraph(
77 "Call it directly from another realm, for example to show a Settler " +
78 "next to an address in your own " + md.InlineCode("Render") + " function:",
79 ))
80 res.Write(md.LanguageCodeBlock("go", goSnippet))
81
82 table := mdtable.Table{Headers: []string{"Function", "Description"}}
83 table.Append([]string{
84 "Markdown(addr, size, transparent)",
85 "The image, or an empty text if there is none",
86 })
87 table.Append([]string{
88 "SVG(addr, size, transparent)",
89 "The SVG document, or an empty text if there is none",
90 })
91 table.Append([]string{
92 "URI(addr, size, transparent)",
93 "The same image as a data URI",
94 })
95 res.Write(table.String() + "\n")
96
97 res.Write(md.Paragraph(
98 "The size is in pixels, from 32 to 2048, and 0 leaves it to the container. " +
99 "With transparent the image has no background. Every call draws the image again and " +
100 "costs gas (about 6 to 8 million), so use it in `Render` and not in a transaction.",
101 ))
102
103 res.Write(md.H2("From a web app"))
104 res.Write(md.Paragraph(
105 "Ask the node for the page of an address, with `vm/qrender`, and use what it returns as an image: " +
106 md.InlineCode(RealmPath+":svg/{address}?size=64&transparent=1") +
107 ". It is empty if the address has no Settler.",
108 ))
109 res.Write(md.Paragraph("With gnokey, from a terminal, save it straight to a file:"))
110 res.Write(md.LanguageCodeBlock("sh", gnokeySnippet))
111
112 if n := settlers.TotalSupply(); n > 0 {
113 res.Write(md.H2("Last Settlers"))
114
115 for id := n; id > 0 && id > n-samples; id-- {
116 tid := grc721.TokenID(strconv.FormatInt(id, 10))
117 owner, err := settlers.OwnerOf(tid)
118 if err != nil {
119 continue
120 }
121
122 tokenPath := settlers.RealmPath + ":token/" + string(tid)
123 table := mdtable.Table{Headers: []string{"", ""}}
124 table.Append([]string{
125 Markdown(owner, sampleSize, false),
126 md.Link(owner.String(), tokenPath),
127 })
128 res.Write(table.String() + "\n")
129 }
130 }
131}
132
133func renderSVG(res *mux.ResponseWriter, req *mux.Request) {
134 svg := SVG(address(req.GetVar("addr")), sizeParam(req), transparentParam(req))
135 res.Write(svg)
136}
137
138func renderMarkdown(res *mux.ResponseWriter, req *mux.Request) {
139 size := sizeParam(req)
140 if size == 0 {
141 size = sampleSize
142 }
143
144 addr := req.GetVar("addr")
145 if !address(addr).IsValid() {
146 res.Write(md.Paragraph("Not a valid address."))
147 res.Write(md.Paragraph(md.Link("< Home", RealmPath)))
148 return
149 }
150
151 res.Write(md.H1("Avatar of " + md.InlineCode(addr)))
152 res.Write(md.Paragraph(Markdown(address(addr), size, transparentParam(req))))
153 res.Write(md.Paragraph(md.Link("< Home", RealmPath)))
154}
155
156func sizeParam(req *mux.Request) int {
157 size, err := strconv.Atoi(req.Query.Get("size"))
158 if err != nil {
159 return 0
160 }
161 return size
162}
163
164func transparentParam(req *mux.Request) bool {
165 v := req.Query.Get("transparent")
166 return v == "1" || v == "true"
167}