render.gno
5.07 Kb · 180 lines
1package nft
2
3import (
4 "strconv"
5
6 "gno.land/p/g17khqpukees4237dtn3astzapmp462vjhsz6st4/settlers/nft/v0"
7 "gno.land/p/moul/md/v0"
8 "gno.land/p/moul/mdtable/v0"
9 "gno.land/p/nt/grc721/v0"
10 "gno.land/p/nt/markdown/sanitize/v0"
11 "gno.land/p/nt/mdalert/v0"
12 "gno.land/p/nt/mux/v0"
13)
14
15const (
16 // Gallery width is the width/height of gallery Settler images.
17 galleryWidth = 96
18
19 // Detail width is the width/height of Settler the image for detail views.
20 detailWidth = 256
21
22 // Gallery length is the number of last minted Settlers of the home page.
23 galleryLen = 5
24)
25
26// Render renders the realm.
27//
28// By default renders the state of the mint and the last minted Settlers.
29//
30// Paths:
31// - `token/{id}`: A Settler with its traits and its owner.
32// - `addr/{address}`: The Settler of an account.
33func Render(path string) string {
34 router := mux.NewRouter()
35 router.NotFoundHandler = func(res *mux.ResponseWriter, _ *mux.Request) {
36 res.Write("Path not found")
37 }
38
39 router.HandleFunc("", renderHome)
40 router.HandleFunc("token/{id}", renderToken)
41 router.HandleFunc("addr/{addr}", renderAddr)
42
43 return router.Render(path)
44}
45
46func renderHome(res *mux.ResponseWriter, _ *mux.Request) {
47 res.Write(md.H1("Settlers"))
48
49 if Notice != "" {
50 res.Write(mdalert.New(NoticeType, "Message", Notice, false).String() + "\n\n")
51 }
52
53 res.Write(md.Paragraph(
54 "Soulbound pixel-art gnomes for the early settlers of Gno.land. " +
55 "Every account that had at least 15 GNOT at the launch of `gnoland-1` can mint one, " +
56 "first come first served. A Settler can't be transferred, it's soulbound",
57 ))
58
59 res.Write(md.Paragraph("Realm paths:"))
60 pathsTable := mdtable.Table{Headers: []string{"Path", "Description"}}
61 pathsTable.Append([]string{
62 md.InlineCode("token/{id}"),
63 "A Settler with its traits and its owner.",
64 })
65 pathsTable.Append([]string{
66 md.InlineCode("addr/{address}"),
67 "The Settler of an account.",
68 })
69 res.Write(pathsTable.String() + "\n")
70
71 minted := tokens.Size()
72 if len(root) == 0 {
73 res.Write(md.Paragraph("The mint is not open yet."))
74 } else {
75 res.Write(md.Paragraph(
76 md.BulletItem(md.Bold("Minted:")+" "+strconv.Itoa(minted)+" of "+strconv.Itoa(supplyCap)) +
77 md.BulletItem(md.Bold("Eligible accounts:")+" "+strconv.Itoa(total)) +
78 md.BulletItem(md.Bold("Merkle root:")+" "+md.InlineCode(MerkleRoot())),
79 ))
80
81 res.Write(md.Paragraph(
82 "To mint, get your proof from the list of eligible accounts and call " +
83 md.Link("Mint", RealmPath+"$help&func=Mint") + " with it; the account that " +
84 "signs the transaction is the one that gets the Settler.",
85 ))
86 res.Write(md.Paragraph(
87 "Check the " + md.Link("README", RealmPath+"$source&file=README.md") + " for the full instructions.",
88 ))
89 }
90
91 if minted > 0 {
92 res.Write(md.H2("Last Settlers"))
93 tokens.ReverseIterateByOffset(0, galleryLen, func(key string, value any) bool {
94 settlerID, _ := strconv.Atoi(key)
95 img := image(settlerID, nft.Unpack(value.(uint32)), galleryWidth)
96 res.Write("[" + img + "](" + tokenPath(settlerID) + ") ")
97 return false
98 })
99 res.Write("\n\n")
100 }
101
102 res.Write(footer())
103}
104
105func renderToken(res *mux.ResponseWriter, req *mux.Request) {
106 tid := grc721.TokenID(req.GetVar("id"))
107 settlerID, _ := parseTokenID(tid)
108 packed, ok := traitsOf(tid)
109 if !ok {
110 res.Write(md.Paragraph("Settler not found."))
111 res.Write(md.Paragraph(backHomeLink()))
112 return
113 }
114
115 res.Write(md.H1(settlerName(settlerID)))
116 t := nft.Unpack(packed)
117 res.Write(image(settlerID, t, detailWidth) + "\n\n")
118
119 ownerAddr, _ := OwnerOf(tid)
120 owner := ownerAddr.String()
121 res.Write(md.Paragraph(
122 "Owner: " + md.Link(owner, RealmPath+":addr/"+owner) +
123 " · soulbound, it can't be transferred",
124 ))
125
126 res.Write("| Trait | Value |\n| --- | --- |\n")
127 for _, attr := range t.Attributes() {
128 res.Write("| " + attr.Type + " | " + attr.Value + " |\n")
129 }
130 res.Write("\n")
131
132 res.Write(md.Paragraph(backHomeLink()))
133 res.Write(footer())
134}
135
136func renderAddr(res *mux.ResponseWriter, req *mux.Request) {
137 addr := sanitize.BechString(req.GetVar("addr"), "g")
138 if addr == "" {
139 res.Write(md.Paragraph("Not a valid address."))
140 res.Write(md.Paragraph(backHomeLink()))
141 return
142 }
143
144 tid := TokenOf(address(addr))
145 if tid == "" {
146 res.Write(md.H1("Settler of " + md.InlineCode(addr)))
147 res.Write(md.Paragraph("This account has not minted a Settler."))
148 res.Write(md.Paragraph(backHomeLink()))
149 return
150 }
151
152 settlerID, _ := parseTokenID(tid)
153 packed, _ := traitsOf(tid)
154 img := image(settlerID, nft.Unpack(packed), detailWidth)
155 res.Write(md.H1("Settler of " + md.InlineCode(addr)))
156 res.Write("[" + img + "](" + tokenPath(settlerID) + ")\n\n")
157 res.Write(md.Paragraph(backHomeLink()))
158 res.Write(footer())
159}
160
161func image(settlerID int, t nft.Traits, width int) string {
162 alt := settlerName(settlerID)
163 return " + ")"
164}
165
166func tokenPath(settlerID int) string {
167 return RealmPath + ":token/" + strconv.Itoa(settlerID)
168}
169
170func backHomeLink() string {
171 return md.Link("< Home", RealmPath)
172}
173
174func settlerName(settlerID int) string {
175 return "Settler #" + strconv.Itoa(settlerID)
176}
177
178func footer() string {
179 return md.Blockquote(nft.Credits)
180}