render.gno
3.66 Kb · 124 lines
1package skins
2
3import "strings"
4
5// Render returns the shop as markdown, for gnoweb:
6//
7// "" the catalog
8// "skin/<id>" one design
9// "player/<addr>" the skins of one address
10//
11// Everything printed went through validate.gno when it was written, or is
12// an address the chain gave the realm; the caller-supplied id and address are
13// printed only once found valid.
14func Render(path string) string {
15 switch {
16 case path == "":
17 return renderHome()
18 case strings.HasPrefix(path, "skin/"):
19 if d := getDesign(strings.TrimPrefix(path, "skin/")); d != nil {
20 return renderDesign(d)
21 }
22 case strings.HasPrefix(path, "player/"):
23 if addr := address(strings.TrimPrefix(path, "player/")); validAddress(addr) {
24 return renderPlayer(addr)
25 }
26 }
27 return "# gnofly skins\n\nPage not found. Go back to the [shop](" + link("") + ").\n"
28}
29
30func link(path string) string {
31 if path == "" {
32 return webPath
33 }
34 return webPath + ":" + path
35}
36
37func renderHome() string {
38 var b strings.Builder
39 b.WriteString("# gnofly skins\n\n")
40 b.WriteString("Paint jobs for the planes of gnofly. Each one bought is a token bound to its buyer: it cannot be\n")
41 b.WriteString("transferred. Buy one with `Buy` and its price attached, then fly it from the hangar of the game.\n")
42 b.WriteString("Classic is free for everyone and is not sold here.\n\n")
43 b.WriteString("| Skin | Model | Price | Sold | Supply | For sale |\n|---|---|---|---|---|---|\n")
44 designs.Iterate("", "", func(_ string, v any) bool {
45 d := v.(*design)
46 b.WriteString("| " + designLink(d) + " | " + d.model + " | " + gnot(d.price) + " | " + itoa(d.minted) +
47 " | " + supplyText(d.maxSupply) + " | " + yesNo(d.listed) + " |\n")
48 return false
49 })
50 b.WriteString("\nTreasury: " + roles.Treasury().String() + "\n")
51 return b.String()
52}
53
54func renderDesign(d *design) string {
55 var b strings.Builder
56 b.WriteString("# " + d.name + "\n\n")
57 b.WriteString("| | |\n|---|---|\n")
58 b.WriteString("| Id | " + d.id + " |\n")
59 b.WriteString("| Model | " + d.model + " |\n")
60 b.WriteString("| Pilot | " + d.pilot + " |\n")
61 b.WriteString("| Body | `" + d.body + "` |\n")
62 b.WriteString("| Wing | `" + d.wing + "` |\n")
63 b.WriteString("| Accent | `" + d.accent + "` |\n")
64 b.WriteString("| Price | " + gnot(d.price) + " |\n")
65 b.WriteString("| Sold | " + itoa(d.minted) + " of " + supplyText(d.maxSupply) + " |\n")
66 b.WriteString("| For sale | " + yesNo(d.listed) + " |\n")
67 b.WriteString("| Metadata | `" + uriBase + d.id + ".json` |\n")
68 b.WriteString("\n[Shop](" + link("") + ")\n")
69 return b.String()
70}
71
72func renderPlayer(addr address) string {
73 var b strings.Builder
74 b.WriteString("# Pilot " + addr.String() + "\n\n")
75 n := 0
76 prefix := addr.String() + "/"
77 owned.Iterate(prefix, addr.String()+"0", func(key string, v any) bool {
78 if n == 0 {
79 b.WriteString("| Skin | Token |\n|---|---|\n")
80 }
81 d := getDesign(strings.TrimPrefix(key, prefix))
82 if d != nil {
83 b.WriteString("| " + designLink(d) + " | " + itoa(v.(int64)) + " |\n")
84 }
85 n++
86 return false
87 })
88 if n == 0 {
89 b.WriteString("No skin yet: this pilot flies Classic.\n")
90 }
91 b.WriteString("\n[Shop](" + link("") + ")\n")
92 return b.String()
93}
94
95func designLink(d *design) string {
96 return "[" + d.name + "](" + link("skin/"+d.id) + ")"
97}
98
99func supplyText(n int64) string {
100 if n == 0 {
101 return "unlimited"
102 }
103 return itoa(n)
104}
105
106// gnot writes ugnot as GNOT: 1000000 -> "1 GNOT", 1500000 -> "1.5 GNOT".
107func gnot(ugnot int64) string {
108 whole, frac := ugnot/1000000, ugnot%1000000
109 if frac == 0 {
110 return itoa(whole) + " GNOT"
111 }
112 f := itoa(frac + 1000000)[1:]
113 for f[len(f)-1] == '0' {
114 f = f[:len(f)-1]
115 }
116 return itoa(whole) + "." + f + " GNOT"
117}
118
119func yesNo(b bool) string {
120 if b {
121 return "yes"
122 }
123 return "no"
124}