Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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 "![" + alt + "](" + nft.RenderDataURI(t, nft.Options{Size: width}) + ")"
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}